C ++ 'da Derleme Zamanı Dizelerini Uygun Şekilde Bildirme


137

C ++ 'da derleme sırasında dizeler oluşturup işleyebilmenin birkaç yararlı uygulaması vardır. C ++ 'da derleme zamanı dizeleri oluşturmak mümkün olsa da, dizgenin değişken bir karakter dizisi olarak bildirilmesi gerektiğinden süreç çok zahmetlidir, örn.

using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;

Dize birleştirme, alt dize çıkarma ve diğerleri gibi işlemler, karakter dizileri üzerinde işlemler olarak kolayca uygulanabilir. Derleme zamanı dizgelerini daha uygun bir şekilde bildirmek mümkün müdür? Değilse, çalışmalarda derleme zamanı dizgilerinin uygun bildirimine izin verecek bir öneri var mı?

Mevcut Yaklaşımlar Neden Başarısız Oldu?

İdeal olarak, derleme zamanı dizelerini aşağıdaki gibi bildirebilmek isteriz:

// Approach 1
using str1 = sequence<"Hello, world!">;

veya kullanıcı tanımlı değişmez değerler kullanarak,

// Approach 2
constexpr auto str2 = "Hello, world!"_s;

decltype(str2)bir constexprkurucu nerede olurdu . Aşağıdakileri yapabileceğiniz gerçeğinden yararlanarak yaklaşım 1'in daha karmaşık bir versiyonunu uygulamak mümkündür:

template <unsigned Size, const char Array[Size]>
struct foo;

Bununla birlikte, dizinin harici bağlantıya sahip olması gerekir, bu nedenle 1. yaklaşımı çalıştırmak için şuna benzer bir şey yazmamız gerekir:

/* Implementation of array to sequence goes here. */

constexpr const char str[] = "Hello, world!";

int main()
{
    using s = string<13, str>;
    return 0;
}

Söylemeye gerek yok, bu çok zahmetli. Yaklaşım 2'nin uygulanması aslında mümkün değildir. Bir ( constexpr) değişmez operatörü bildirecek olsaydık, dönüş türünü nasıl belirtirdik? Operatörün değişken bir karakter dizisi döndürmesine ihtiyacımız olduğundan const char*, dönüş türünü belirtmek için parametreyi kullanmamız gerekir :

constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */

Bu, bir derleme hatasıyla sonuçlanır, çünkü sbir constexpr. Aşağıdakileri yaparak bu sorunu çözmeye çalışmak pek yardımcı olmuyor.

template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }

Standart, bu belirli değişmez operatör formunun tamsayı ve kayan nokta türleri için ayrıldığını belirtir. Çalışırken 123_sişe abc_syaramazdı. Ya kullanıcı tanımlı değişmez değerleri tamamen kaldırırsak ve yalnızca normal birconstexpr işlevi kullanırsak?

template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */

Daha önce olduğu gibi, dizinin artık parametrenin bir parametresi olduğu sorunuyla karşılaşıyoruz. constexpr fonksiyonun kendisinin artık bir constexprtür olmadığı .

Bir dizgeyi ve dizgenin boyutunu bağımsız değişken olarak alan ve dizedeki karakterlerden oluşan bir dizi döndüren ( BOOST_PP_FORdizgiselleştirme, dizi alt dizeleri ve benzerlerini kullanarak) bir C önişlemci makrosu tanımlamanın mümkün olduğuna inanıyorum . Ancak, böyle bir makroyu uygulamak için zamanım (veya yeterince ilgim) yok =)


2
Boost, sabit bir ifade olarak kullanılabilen bir dizeyi tanımlayan bir makroya sahiptir. Bir dizge üyesi olan bir sınıfı tanımlar. Kontrol ettin mi?
Yayınlanma tarihi:


1
Stack Overflow, bir şey için bir teklif olup olmadığını sormak için uygun bir yer değildir. Bunun için en iyi yer C ++ sitesi olacaktır .
Nicol Bolas

1
Temel olarak, dizi / ptr'de depolanan karakterleri bir parametre paketine (Xeo'nun yaptığı gibi) genişletirsiniz. Tür olmayan şablon argümanlarına bölünmemiş olsalar da, bunları constexprişlevler içinde kullanabilir ve dizileri başlatabilirsiniz (dolayısıyla, concat, substr vb.).
27'de dyp

1
@MareInfinitus Kısacası, constexprsonuçlara bağlı olarak farklı kod yolları alabilmeniz için dizeler derleme sırasında ayrıştırılabilir. Esasen, C ++ içinde EDL'ler oluşturabilirsiniz; uygulamalar oldukça sınırsızdır.
void-pointer

Yanıtlar:


127

C ++ Now 2012'de sunulan Scott Schurr'ınstr_const zarafetiyle eşleşen hiçbir şey görmedim . Gerçi gerektirir .constexpr

İşte onu nasıl kullanabileceğiniz ve neler yapabileceği:

int
main()
{
    constexpr str_const my_string = "Hello, world!";
    static_assert(my_string.size() == 13, "");
    static_assert(my_string[4] == 'o', "");
    constexpr str_const my_other_string = my_string;
    static_assert(my_string == my_other_string, "");
    constexpr str_const world(my_string, 7, 5);
    static_assert(world == "world", "");
//  constexpr char x = world[5]; // Does not compile because index is out of range!
}

Derleme zamanı aralığı kontrolünden çok daha havalı olamaz!

Hem kullanım hem de uygulama makro içermez. Ve ip boyutunda yapay bir sınır yoktur. Uygulamayı burada yayınlardım, ancak Scott'ın zımni telif hakkına saygı duyuyorum. Uygulama, yukarıda bağlantılı olarak sunumunun tek bir slaytındadır.


3
Yeni constexpr dizeleri (dize birleştirme ve alt dize çıkarma gibi) oluşturan işlemler bu yaklaşımla çalışabilir mi? Belki iki constexpr-string sınıfı kullanarak (biri temel alır str_const, diğeri temel alır sequence) bu mümkün olabilir. Kullanıcı str_constdizeyi başlatmak için kullanacaktır , ancak yeni dizeler oluşturan sonraki işlemler sequencenesneleri döndürür .
void-pointer

5
Bu iyi bir kod parçasıdır. Bununla birlikte, bu yaklaşım, şablon parametreleri olarak bir karakter dizisi ile bildirilen bir dizgeye kıyasla hala bir kusura sahiptir: bir str_const sabit bir değerdir ve bir tür değildir, bu nedenle birçok meta programlama deyiminin kullanılmasını engeller.
Jean-Bernard Jansen

1
@JBJansen, karma işlevler olmadan, bir dizeyi daha sonra şablon parametresi olarak kullanılabilecek bir türe derlemek mümkündür. Her farklı dize farklı bir tür verir. Temel fikir, dizeyi bir karakter paketine dönüştürmektir template<char... cs>. Teorik olarak, değişmez bir dizgeyi alan ve içeriği bir işleve göre derleyen bir şey oluşturabilirsiniz. Dyp ile cevabı görün. Oldukça eksiksiz görünümlü bir kütüphane metaparse . Esasen, değişmez dizelerden türlere kadar her türlü eşlemeyi tanımlayabilir ve bu tür bir teknoloji ile uygulayabilirsiniz.
Aaron McDaid

1
Coşkuyu paylaşmıyorum… şablon meta işlevleriyle çalışmıyor - çalışma zamanında constexpr işlevlerinin çağrılabilir olacağına dair aptalca uzlaşma nedeniyle çok can sıkıcı - gerçek bir birleştirme yok, bir char dizisinin tanımlanmasını gerektirmiyor (başlıkta çirkin) - buna rağmen yukarıda bahsedilen constexpr uzlaşması sayesinde makrosuz çözümlerin çoğu için doğrudur - ve aralık kontrolü beni pek etkilemiyor çünkü düşük düzeydeki constexpr const char * bile buna sahip. Bir dizi tanımlaması pahasına bir değişmez değerden (bir meta işlev kullanarak) da yapılabilen kendi parametre paketi dizemi aldım.
Arne Vogel

2
@ user975326: Bu uygulamamı az önce gözden geçirdim ve bir constexpr operator==. Afedersiniz. Scott'ın sunumu, bunu nasıl yapacağınıza başlamanızı sağlamalıdır. C ++ 14'te C ++ 11'e göre çok daha kolaydır. C ++ 11'de denemekten bile rahatsız olmazdım. Scott'ın son constexprkonuşmalarına buradan
göz atın

41

Bir dizgeyi ve dizgenin boyutunu bağımsız değişken olarak alan ve dizedeki karakterlerden oluşan bir dizi döndüren (BOOST_PP_FOR, dizgeleştirme, dizi alt dizeleri ve benzerlerini kullanarak) bir C önişlemci makrosu tanımlamanın mümkün olması gerektiğine inanıyorum. Ancak, böyle bir makroyu uygulamak için zamanım (veya yeterince ilgim) yok

Bunu, çok basit makro ve bazı C ++ 11 özelliklerini kullanarak, artırmaya güvenmeden uygulamak mümkündür:

  1. lambdas variadic
  2. şablonları
  3. genelleştirilmiş sabit ifadeler
  4. statik olmayan veri üyesi başlatıcılar
  5. tek tip başlatma

(son ikisi burada kesinlikle gerekli değildir)

  1. Kullanıcı tarafından sağlanan 0'dan N'ye kadar olan göstergelerle değişken bir şablonu başlatabilmemiz gerekir - bu, aynı zamanda, örneğin demeti değişken şablon işlevinin bağımsız değişkenine genişletmek için de yararlı bir araç (bkz. sorular: Bir demeti, değişken şablon işlevinin bağımsız değişkenlerine nasıl genişletirim?
    " "bir demet açma eşleşen bir işlev işaretçisi çağırmak )

    namespace  variadic_toolbox
    {
        template<unsigned  count, 
            template<unsigned...> class  meta_functor, unsigned...  indices>
        struct  apply_range
        {
            typedef  typename apply_range<count-1, meta_functor, count-1, indices...>::result  result;
        };
    
        template<template<unsigned...> class  meta_functor, unsigned...  indices>
        struct  apply_range<0, meta_functor, indices...>
        {
            typedef  typename meta_functor<indices...>::result  result;
        };
    }
  2. daha sonra, char türünde olmayan parametre ile string adında değişken bir şablon tanımlayın:

    namespace  compile_time
    {
        template<char...  str>
        struct  string
        {
            static  constexpr  const char  chars[sizeof...(str)+1] = {str..., '\0'};
        };
    
        template<char...  str>
        constexpr  const char  string<str...>::chars[sizeof...(str)+1];
    }
  3. şimdi en ilginç kısım - karakter değişmezlerini dize şablonuna geçirmek:

    namespace  compile_time
    {
        template<typename  lambda_str_type>
        struct  string_builder
        {
            template<unsigned... indices>
            struct  produce
            {
                typedef  string<lambda_str_type{}.chars[indices]...>  result;
            };
        };
    }
    
    #define  CSTRING(string_literal)                                                        \
        []{                                                                                 \
            struct  constexpr_string_type { const char * chars = string_literal; };         \
            return  variadic_toolbox::apply_range<sizeof(string_literal)-1,                 \
                compile_time::string_builder<constexpr_string_type>::produce>::result{};    \
        }()

basit bir birleştirme gösterimi, kullanımı gösterir:

    namespace  compile_time
    {
        template<char...  str0, char...  str1>
        string<str0..., str1...>  operator*(string<str0...>, string<str1...>)
        {
            return  {};
        }
    }

    int main()
    {
        auto  str0 = CSTRING("hello");
        auto  str1 = CSTRING(" world");

        std::cout << "runtime concat: " <<  str_hello.chars  << str_world.chars  << "\n <=> \n";
        std::cout << "compile concat: " <<  (str_hello * str_world).chars  <<  std::endl;
    }

https://ideone.com/8Ft2xu


1
Bu o kadar basit ki hala işe yaradığına inanamıyorum. 1! Bir şey: işaretsiz yerine size_t kullanmamalı mısınız?
kirbyfan64sos

1
Peki operator+yerine kullanmaya ne dersiniz operator*? (str_hello + str_world)
Remy Lebeau

Bu çözümü popüler Scott Schurr'ın str_const yöntemine tercih ediyorum, çünkü bu yöntem temeldeki verilerin constexpr olmasını sağlıyor. Schurr'un yöntemi çalışma zamanında bir char [] yığın değişkeni ile bir str_const oluşturmama izin veriyor; Bir işlevden güvenli bir şekilde str_const döndüremiyorum veya başka bir evreye geçiremiyorum.
Glenn

Bağlantı öldü ... herhangi biri yeniden gönderebilir mi? @Glenn?
einpoklum

CSTRINGMakronuzda lambda etrafına fazladan bir çift küme parantezi eklemelisiniz . Aksi takdirde , çift öznitelikler için ayrıldığından , CSTRINGbir []operatöre iç çağrı oluşturamazsınız [[.
florestan

21

Düzenleme: Howard Hinnant'ın (ve bir şekilde OP ile ilgili yorumumda benim) belirttiği gibi, dizenin her bir karakterini tek bir şablon argümanı olarak içeren bir türe ihtiyacınız olmayabilir. Buna ihtiyacınız varsa, aşağıda makro içermeyen bir çözüm var.

Derleme zamanında dizelerle çalışmaya çalışırken bulduğum bir numara var. "Şablon dizisi" dışında başka bir tür tanıtılması gerekir, ancak işlevler içinde bu türün kapsamını sınırlayabilirsiniz.

Makroları değil, bazı C ++ 11 özelliklerini kullanır.

#include <iostream>

// helper function
constexpr unsigned c_strlen( char const* str, unsigned count = 0 )
{
    return ('\0' == str[0]) ? count : c_strlen(str+1, count+1);
}

// helper "function" struct
template < char t_c, char... tt_c >
struct rec_print
{
    static void print()
    {
        std::cout << t_c;
        rec_print < tt_c... > :: print ();
    }
};
    template < char t_c >
    struct rec_print < t_c >
    {
        static void print() { std::cout << t_c; }
    };


// destination "template string" type
template < char... tt_c >
struct exploded_string
{
    static void print()
    {
        rec_print < tt_c... > :: print();
    }
};

// struct to explode a `char const*` to an `exploded_string` type
template < typename T_StrProvider, unsigned t_len, char... tt_c >
struct explode_impl
{
    using result =
        typename explode_impl < T_StrProvider, t_len-1,
                                T_StrProvider::str()[t_len-1],
                                tt_c... > :: result;
};

    template < typename T_StrProvider, char... tt_c >
    struct explode_impl < T_StrProvider, 0, tt_c... >
    {
         using result = exploded_string < tt_c... >;
    };

// syntactical sugar
template < typename T_StrProvider >
using explode =
    typename explode_impl < T_StrProvider,
                            c_strlen(T_StrProvider::str()) > :: result;


int main()
{
    // the trick is to introduce a type which provides the string, rather than
    // storing the string itself
    struct my_str_provider
    {
        constexpr static char const* str() { return "hello world"; }
    };

    auto my_str = explode < my_str_provider >{};    // as a variable
    using My_Str = explode < my_str_provider >;    // as a type

    my_str.print();
}

1
Hafta sonunu bağımsız olarak benzer bir kod parçası geliştirerek ve tür dizgilerini ayrıştırmak için çok basit bir sistem yaparak geçirdim, örn pair<int,pair<char,double>>. Kendimle gurur duydum ve sonra bu cevabı keşfettim ve metaparse bugün kütüphanesini ! Bunun gibi aptalca projelere başlamadan önce gerçekten SO'yu daha derinlemesine araştırmalıyım :-) Teorik olarak, bu tür bir teknolojiden tam bir C ++ derleyicisinin oluşturulabileceğini tahmin ediyorum. Bununla inşa edilen en çılgınca şey nedir?
Aaron McDaid

Bilmiyorum. Bu teknikleri gerçek dünya projesinde hiç kullanmadım, bu yüzden yaklaşımı takip etmedim. Yine de biraz daha uygun olan yerel tipte bir numara değişikliğini hatırladığımı düşünüyorum .. belki yerel bir statik char[].
dyp

Şunu Do my_str.print();yerinestr.print(); ?
Mike

C ++ 14'ün biraz daha kısa versiyonu var mı?
Mike

Sağlayıcı yapmak zorunda olmanız çok yazık (en azından C ++ 11'de) - Aynı ifadede bir dizeyi kullanabilmeyi gerçekten çok isterim: /
Alec Teal

10

Boost çözümünü kullanmak istemiyorsanız, benzer bir şey yapacak basit makrolar oluşturabilirsiniz:

#define MACRO_GET_1(str, i) \
    (sizeof(str) > (i) ? str[(i)] : 0)

#define MACRO_GET_4(str, i) \
    MACRO_GET_1(str, i+0),  \
    MACRO_GET_1(str, i+1),  \
    MACRO_GET_1(str, i+2),  \
    MACRO_GET_1(str, i+3)

#define MACRO_GET_16(str, i) \
    MACRO_GET_4(str, i+0),   \
    MACRO_GET_4(str, i+4),   \
    MACRO_GET_4(str, i+8),   \
    MACRO_GET_4(str, i+12)

#define MACRO_GET_64(str, i) \
    MACRO_GET_16(str, i+0),  \
    MACRO_GET_16(str, i+16), \
    MACRO_GET_16(str, i+32), \
    MACRO_GET_16(str, i+48)

#define MACRO_GET_STR(str) MACRO_GET_64(str, 0), 0 //guard for longer strings

using seq = sequence<MACRO_GET_STR("Hello world!")>;

Tek sorun, 64 karakterlik sabit boyuttur (artı ek sıfır). Ancak ihtiyaçlarınıza göre kolaylıkla değiştirilebilir.


Bu çözümü çok beğendim; çok basit ve işi zarif bir şekilde yapıyor. Makroyu hiçbir şey eklenmeyecek şekilde değiştirmek mümkün mü sizeof(str) > i(fazladan 0,jeton eklemek yerine )? trimMakro çağrıldıktan sonra bunu yapacak bir meta işlevi tanımlamak kolaydır , ancak makronun kendisi değiştirilebilseydi iyi olurdu.
void-pointer

İmkansızdır çünkü ayrıştırıcı anlamaz sizeof(str). Dize boyutunu manuel olarak eklemek mümkündür, MACRO_GET_STR(6, "Hello")ancak bu, Boost makrolarının çalışmasını gerektirir çünkü manuel olarak yazmak 100 kat daha fazla kod gerektirir (buna benzer basit bir şey gerekir 1+1).
Yankes

6

Bir dizgeyi ve dizenin boyutunu bağımsız değişken olarak alan ve dizedeki karakterlerden oluşan bir dizi döndüren (BOOST_PP_FOR, dizgeleştirme, dizi alt dizeleri ve benzerlerini kullanarak) bir C önişlemci makrosu tanımlamanın mümkün olması gerektiğine inanıyorum.

Bir makale var: Abel Sinkovics ve Dave Abrahams'ın C ++ şablonu metaprogramlarında dizeleri kullanma .

Makro + BOOST_PP_REPEAT kullanma fikrinize göre bazı iyileştirmeler var - makroya açık boyut geçirmeyi gerektirmez. Kısaca, dizi boyutu ve "dizi aşımı koruması" için sabit üst sınıra dayanır:

template <int N>
constexpr char at(char const(&s)[N], int i)
{
    return i >= N ? '\0' : s[i];
}

artı koşullu yükseltme :: mpl :: push_back .


Kabul edilen cevabımı Yankes'in çözümüne değiştirdim, çünkü bu özel sorunu çözüyor ve çok zarif bir şekilde constexpr veya karmaşık önişlemci kodu kullanmadan yapıyor.

Sondaki sıfırları, elle yazılmış makro döngüsünü, genişletilmiş makroda dizenin 2x tekrarını kabul ederseniz ve Boost'a sahip değilseniz - o zaman kabul ediyorum - daha iyidir. Yine de, Boost ile sadece üç satır olurdu:

CANLI DEMO

#include <boost/preprocessor/repetition/repeat.hpp>
#define GET_STR_AUX(_, i, str) (sizeof(str) > (i) ? str[(i)] : 0),
#define GET_STR(str) BOOST_PP_REPEAT(64,GET_STR_AUX,str) 0

Burada ilk çalışma örneğini verdiği için çözümü başlangıçta Yankes'in olarak değiştirdim. Bu noktada, birçok iyi rekabet eden fikir var. Cevabı bu kadar erken seçmek benim hatamdı. Şu anda bu soruyu cevaplanmamış olarak belirteceğim ve herkesin burada yayınladığı fikirleri denemeye zaman bulana kadar bekleyeceğim. İnsanların burada verdiği cevaplarda pek çok yararlı bilgi var ...
void-pointer

Katılıyorum - örneğin, Howard Hinnant örneğini seviyorum.
Evgeny Panasyuk

5

Kimse diğer cevabımı beğenmiyor gibi görünüyor: - <. İşte burada str_const'u gerçek türe nasıl dönüştüreceğimi gösteriyorum:

#include <iostream>
#include <utility>

// constexpr string with const member functions
class str_const { 
private:
    const char* const p_;
    const std::size_t sz_;
public:

    template<std::size_t N>
    constexpr str_const(const char(&a)[N]) : // ctor
    p_(a), sz_(N-1) {}

    constexpr char operator[](std::size_t n) const { 
        return n < sz_ ? p_[n] :
        throw std::out_of_range("");
    }

    constexpr std::size_t size() const { return sz_; } // size()
};


template <char... letters>
struct string_t{
    static char const * c_str() {
        static constexpr char string[]={letters...,'\0'};
        return string;
    }
};

template<str_const const& str,std::size_t... I>
auto constexpr expand(std::index_sequence<I...>){
    return string_t<str[I]...>{};
}

template<str_const const& str>
using string_const_to_type = decltype(expand<str>(std::make_index_sequence<str.size()>{}));

constexpr str_const hello{"Hello World"};
using hello_t = string_const_to_type<hello>;

int main()
{
//    char c = hello_t{};        // Compile error to print type
    std::cout << hello_t::c_str();
    return 0;
}

Clang ++ -stdlib = libc ++ -std = c ++ 14 (clang 3.7) ile derler


İyi çalışıyor, ancak msvc 2019 için değil, str.size () 'nin constexpr olmadığından şikayet ediyor. Str.size () ayrı ayrı çıkarıldıktan sonra 2 ekleyerek sabitlenebilir. Belki bu bazı olumlu oyları geri aldı ;-)
Zacharias

4

Bir meslektaşım, derleme zamanında bellekteki dizeleri birleştirmem için bana meydan okudu. Derleme zamanında tek tek dizelerin başlatılmasını da içerir. Tam kod listesi burada:

//Arrange strings contiguously in memory at compile-time from string literals.
//All free functions prefixed with "my" to faciliate grepping the symbol tree
//(none of them should show up).

#include <iostream>

using std::size_t;

//wrapper for const char* to "allocate" space for it at compile-time
template<size_t N>
struct String {
    //C arrays can only be initialised with a comma-delimited list
    //of values in curly braces. Good thing the compiler expands
    //parameter packs into comma-delimited lists. Now we just have
    //to get a parameter pack of char into the constructor.
    template<typename... Args>
    constexpr String(Args... args):_str{ args... } { }
    const char _str[N];
};

//takes variadic number of chars, creates String object from it.
//i.e. myMakeStringFromChars('f', 'o', 'o', '\0') -> String<4>::_str = "foo"
template<typename... Args>
constexpr auto myMakeStringFromChars(Args... args) -> String<sizeof...(Args)> {
    return String<sizeof...(args)>(args...);
}

//This struct is here just because the iteration is going up instead of
//down. The solution was to mix traditional template metaprogramming
//with constexpr to be able to terminate the recursion since the template
//parameter N is needed in order to return the right-sized String<N>.
//This class exists only to dispatch on the recursion being finished or not.
//The default below continues recursion.
template<bool TERMINATE>
struct RecurseOrStop {
    template<size_t N, size_t I, typename... Args>
    static constexpr String<N> recurseOrStop(const char* str, Args... args);
};

//Specialisation to terminate recursion when all characters have been
//stripped from the string and converted to a variadic template parameter pack.
template<>
struct RecurseOrStop<true> {
    template<size_t N, size_t I, typename... Args>
    static constexpr String<N> recurseOrStop(const char* str, Args... args);
};

//Actual function to recurse over the string and turn it into a variadic
//parameter list of characters.
//Named differently to avoid infinite recursion.
template<size_t N, size_t I = 0, typename... Args>
constexpr String<N> myRecurseOrStop(const char* str, Args... args) {
    //template needed after :: since the compiler needs to distinguish
    //between recurseOrStop being a function template with 2 paramaters
    //or an enum being compared to N (recurseOrStop < N)
    return RecurseOrStop<I == N>::template recurseOrStop<N, I>(str, args...);
}

//implementation of the declaration above
//add a character to the end of the parameter pack and recurse to next character.
template<bool TERMINATE>
template<size_t N, size_t I, typename... Args>
constexpr String<N> RecurseOrStop<TERMINATE>::recurseOrStop(const char* str,
                                                            Args... args) {
    return myRecurseOrStop<N, I + 1>(str, args..., str[I]);
}

//implementation of the declaration above
//terminate recursion and construct string from full list of characters.
template<size_t N, size_t I, typename... Args>
constexpr String<N> RecurseOrStop<true>::recurseOrStop(const char* str,
                                                       Args... args) {
    return myMakeStringFromChars(args...);
}

//takes a compile-time static string literal and returns String<N> from it
//this happens by transforming the string literal into a variadic paramater
//pack of char.
//i.e. myMakeString("foo") -> calls myMakeStringFromChars('f', 'o', 'o', '\0');
template<size_t N>
constexpr String<N> myMakeString(const char (&str)[N]) {
    return myRecurseOrStop<N>(str);
}

//Simple tuple implementation. The only reason std::tuple isn't being used
//is because its only constexpr constructor is the default constructor.
//We need a constexpr constructor to be able to do compile-time shenanigans,
//and it's easier to roll our own tuple than to edit the standard library code.

//use MyTupleLeaf to construct MyTuple and make sure the order in memory
//is the same as the order of the variadic parameter pack passed to MyTuple.
template<typename T>
struct MyTupleLeaf {
    constexpr MyTupleLeaf(T value):_value(value) { }
    T _value;
};

//Use MyTupleLeaf implementation to define MyTuple.
//Won't work if used with 2 String<> objects of the same size but this
//is just a toy implementation anyway. Multiple inheritance guarantees
//data in the same order in memory as the variadic parameters.
template<typename... Args>
struct MyTuple: public MyTupleLeaf<Args>... {
    constexpr MyTuple(Args... args):MyTupleLeaf<Args>(args)... { }
};

//Helper function akin to std::make_tuple. Needed since functions can deduce
//types from parameter values, but classes can't.
template<typename... Args>
constexpr MyTuple<Args...> myMakeTuple(Args... args) {
    return MyTuple<Args...>(args...);
}

//Takes a variadic list of string literals and returns a tuple of String<> objects.
//These will be contiguous in memory. Trailing '\0' adds 1 to the size of each string.
//i.e. ("foo", "foobar") -> (const char (&arg1)[4], const char (&arg2)[7]) params ->
//                       ->  MyTuple<String<4>, String<7>> return value
template<size_t... Sizes>
constexpr auto myMakeStrings(const char (&...args)[Sizes]) -> MyTuple<String<Sizes>...> {
    //expands into myMakeTuple(myMakeString(arg1), myMakeString(arg2), ...)
    return myMakeTuple(myMakeString(args)...);
}

//Prints tuple of strings
template<typename T> //just to avoid typing the tuple type of the strings param
void printStrings(const T& strings) {
    //No std::get or any other helpers for MyTuple, so intead just cast it to
    //const char* to explore its layout in memory. We could add iterators to
    //myTuple and do "for(auto data: strings)" for ease of use, but the whole
    //point of this exercise is the memory layout and nothing makes that clearer
    //than the ugly cast below.
    const char* const chars = reinterpret_cast<const char*>(&strings);
    std::cout << "Printing strings of total size " << sizeof(strings);
    std::cout << " bytes:\n";
    std::cout << "-------------------------------\n";

    for(size_t i = 0; i < sizeof(strings); ++i) {
        chars[i] == '\0' ? std::cout << "\n" : std::cout << chars[i];
    }

    std::cout << "-------------------------------\n";
    std::cout << "\n\n";
}

int main() {
    {
        constexpr auto strings = myMakeStrings("foo", "foobar",
                                               "strings at compile time");
        printStrings(strings);
    }

    {
        constexpr auto strings = myMakeStrings("Some more strings",
                                               "just to show Jeff to not try",
                                               "to challenge C++11 again :P",
                                               "with more",
                                               "to show this is variadic");
        printStrings(strings);
    }

    std::cout << "Running 'objdump -t |grep my' should show that none of the\n";
    std::cout << "functions defined in this file (except printStrings()) are in\n";
    std::cout << "the executable. All computations are done by the compiler at\n";
    std::cout << "compile-time. printStrings() executes at run-time.\n";
}

Derleme zamanında bittiğine emin misin? Bir süre önce bununla ilgili bir tartışma oldu ve bana göre sonuç net değil.
2013

1
Koşmak objdump -t a.out |grep myhiçbir şey bulamaz. Bu kodu yazmaya başladığımda constexpr, işlevlerden kaldırmayı denemeye devam ettim ve objdumponlara ne zaman constexpratlandığını gösterdim . Derleme zamanında olacağından% 99,9 eminim.
Átila Neves

1
Disassembly ( -S) 'e bakarsanız, gcc (4.7.2)' nin gerçekten constexprderleme zamanında işlevleri çözdüğünü fark edeceksiniz . Oysa dizeleri vardır değil derleme zamanında toplandı. Daha ziyade, (eğer doğru yorumluyorsam) bu "birleştirilmiş" dizelerin her bir karakteri movbiçin, aradığınız muhtemelen optimizasyon olan kendi işlemi vardır .
2013

2
Bu doğru. Gcc 4.9 ile tekrar denedim ve hala aynı şeyi yapıyor. Her zaman bu derleyicinin aptal olduğunu düşünmüşümdür. Sadece dün farklı bir derleyici denemeyi düşündüm. Clang ile, geleneksel hareketler hiç yok. Gcc ile -Os da onlardan kurtulur, ancak -O3 aynı şeyi yapar.
Átila Neves

4

İşte geçirilen her derleme zamanı dizesi için bir std :: tuple <char ...> oluşturmaya yönelik kısa ve öz bir C ++ 14 çözümü.

#include <tuple>
#include <utility>


namespace detail {
        template <std::size_t ... indices>
        decltype(auto) build_string(const char * str, std::index_sequence<indices...>) {
                return std::make_tuple(str[indices]...);
        }
}

template <std::size_t N>
constexpr decltype(auto) make_string(const char(&str)[N]) {
        return detail::build_string(str, std::make_index_sequence<N>());
}

auto HelloStrObject = make_string("hello");

Ve işte benzersiz bir derleme zamanı türü oluşturmak için bir tane, diğer makro gönderisinden kırpılmış.

#include <utility>

template <char ... Chars>
struct String {};

template <typename Str, std::size_t ... indices>
decltype(auto) build_string(std::index_sequence<indices...>) {
        return String<Str().chars[indices]...>();
}

#define make_string(str) []{\
        struct Str { const char * chars = str; };\
        return build_string<Str>(std::make_index_sequence<sizeof(str)>());\
}()

auto HelloStrObject = make_string("hello");

Kullanıcı tanımlı değişmez değerlerin henüz bunun için kullanılamaması gerçekten çok kötü.


Aslında, GCC / Clang tarafından desteklenen bir uzantıyı kullanabilirler, ancak bir yanıt olarak göndermeden önce bunun standarda eklenmesini bekleyeceğim.
void-pointer

2

Howard Hinnant'ın fikrine dayanarak, iki değişmezi bir araya getirecek bir edebi sınıf oluşturabilirsiniz.

template<int>
using charDummy = char;

template<int... dummy>
struct F
{
    const char table[sizeof...(dummy) + 1];
    constexpr F(const char* a) : table{ str_at<dummy>(a)..., 0}
    {

    }
    constexpr F(charDummy<dummy>... a) : table{ a..., 0}
    {

    }

    constexpr F(const F& a) : table{ a.table[dummy]..., 0}
    {

    }

    template<int... dummyB>
    constexpr F<dummy..., sizeof...(dummy)+dummyB...> operator+(F<dummyB...> b)
    {
        return { this->table[dummy]..., b.table[dummyB]... };
    }
};

template<int I>
struct get_string
{
    constexpr static auto g(const char* a) -> decltype( get_string<I-1>::g(a) + F<0>(a + I))
    {
        return get_string<I-1>::g(a) + F<0>(a + I);
    }
};

template<>
struct get_string<0>
{
    constexpr static F<0> g(const char* a)
    {
        return {a};
    }
};

template<int I>
constexpr auto make_string(const char (&a)[I]) -> decltype( get_string<I-2>::g(a) )
{
    return get_string<I-2>::g(a);
}

constexpr auto a = make_string("abc");
constexpr auto b = a+ make_string("def"); // b.table == "abcdef" 

nereden str_atgeliyor
mic_e

onun gibi bir şey:str_at<int I>(const char* a) { return a[i]; }
Yankes

2

1. yaklaşımınız doğru olanıdır.

Bununla birlikte, dizinin harici bağlantıya sahip olması gerekir, bu nedenle yaklaşım 1'in çalışması için şöyle bir şey yazmamız gerekir: constexpr const char str [] = "Merhaba, dünya!";

Hayır, doğru değil. Bu, clang ve gcc ile derlenir. Umarım standart c ++ 11'dir, ancak ben bir dil katmanı değilim.

#include <iostream>

template <char... letters>
struct string_t{
    static char const * c_str() {
        static constexpr char string[]={letters...,'\0'};
        return string;
    }
};

// just live with it, but only once
using Hello_World_t = string_t<'H','e','l','l','o',' ','w','o','r','l','d','!'>;

template <typename Name>
void print()
{
    //String as template parameter
    std::cout << Name::c_str();
}

int main() {
    std::cout << Hello_World_t::c_str() << std::endl;
    print<Hello_World_t>();
    return 0;
}

C ++ 17 için gerçekten sevdiğim şey şunun eşdeğer olması olurdu (1. yaklaşımı tamamlamak için)

// for template <char...>
<"Text"> == <'T','e','x','t'>

Void-pointer'ın da bahsettiği gibi, şablonlu kullanıcı tanımlı değişmez değerler standardında çok benzer bir şey zaten mevcuttur, ancak yalnızca rakamlar için. O zamana kadar başka bir küçük numara, geçersiz kılma düzenleme modunu kullanmak + kopyalayıp yapıştırmaktır.

string_t<' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '>;

Makroya aldırış etmezseniz, bu işe yarar (Yankes cevabından biraz değiştirilmiştir):

#define MACRO_GET_1(str, i) \
(sizeof(str) > (i) ? str[(i)] : 0)

#define MACRO_GET_4(str, i) \
MACRO_GET_1(str, i+0),  \
MACRO_GET_1(str, i+1),  \
MACRO_GET_1(str, i+2),  \
MACRO_GET_1(str, i+3)

#define MACRO_GET_16(str, i) \
MACRO_GET_4(str, i+0),   \
MACRO_GET_4(str, i+4),   \
MACRO_GET_4(str, i+8),   \
MACRO_GET_4(str, i+12)

#define MACRO_GET_64(str, i) \
MACRO_GET_16(str, i+0),  \
MACRO_GET_16(str, i+16), \
MACRO_GET_16(str, i+32), \
MACRO_GET_16(str, i+48)

//CT_STR means Compile-Time_String
#define CT_STR(str) string_t<MACRO_GET_64(#str, 0), 0 >//guard for longer strings

print<CT_STR(Hello World!)>();

2

kacey'nin benzersiz bir derleme zamanı türü oluşturmaya yönelik çözümü, küçük değişikliklerle C ++ 11 ile de kullanılabilir:

template <char... Chars>
struct string_t {};

namespace detail {
template <typename Str,unsigned int N,char... Chars>
struct make_string_t : make_string_t<Str,N-1,Str().chars[N-1],Chars...> {};

template <typename Str,char... Chars>
struct make_string_t<Str,0,Chars...> { typedef string_t<Chars...> type; };
} // namespace detail

#define CSTR(str) []{ \
    struct Str { const char *chars = str; }; \
    return detail::make_string_t<Str,sizeof(str)>::type(); \
  }()

kullanın:

template <typename String>
void test(String) {
  // ... String = string_t<'H','e','l','l','o','\0'>
}

test(CSTR("Hello"));

2

Boost hana haritası ile oynarken bu konuya rastladım. Cevapların hiçbiri sorunumu çözmediğinden, başkaları için potansiyel olarak yararlı olabileceği için buraya eklemek istediğim farklı bir çözüm buldum.

Benim sorunum, hana dizgileriyle boost hana haritasını kullanırken, derleyicinin hala bazı çalışma zamanı kodları oluşturmasıydı (aşağıya bakın). Bunun nedeni, haritayı derleme sırasında sorgulamak için olması gerektiğiydi constexpr. Gibi bu mümkün değildir BOOST_HANA_STRINGmakro kullanılamaz bir lambda oluşturur constexprbağlamda. Öte yandan, haritanın farklı türde olması için farklı içeriğe sahip dizelere ihtiyacı vardır.

Bu konudaki çözümler ya bir lambda kullanıyor ya da farklı içerikler için farklı türler sağlamıyor, aşağıdaki yaklaşımı yararlı buldum. Ayrıca hacky str<'a', 'b', 'c'>sözdiziminden kaçınır .

Temel fikir, str_constkarakterlerin karması üzerine Scott Schurr'ın bir versiyonuna sahip olmaktır . Bu c++14, ancak işlevin c++11özyinelemeli bir uygulamasıyla mümkün olmalıdır crc32( buraya bakın ).

// str_const from https://github.com/boostcon/cppnow_presentations_2012/blob/master/wed/schurr_cpp11_tools_for_class_authors.pdf?raw=true

    #include <string>

template<unsigned Hash>  ////// <- This is the difference...
class str_const2 { // constexpr string
private:
    const char* const p_;
    const std::size_t sz_;
public:
    template<std::size_t N>
    constexpr str_const2(const char(&a)[N]) : // ctor
        p_(a), sz_(N - 1) {}


    constexpr char operator[](std::size_t n) const { // []
        return n < sz_ ? p_[n] :
            throw std::out_of_range("");
    }

    constexpr std::size_t size() const { return sz_; } // size()

    constexpr const char* const data() const {
        return p_;
    }
};

// Crc32 hash function. Non-recursive version of https://stackoverflow.com/a/23683218/8494588
static constexpr unsigned int crc_table[256] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};

template<size_t N>
constexpr auto crc32(const char(&str)[N])
{
    unsigned int prev_crc = 0xFFFFFFFF;
    for (auto idx = 0; idx < sizeof(str) - 1; ++idx)
        prev_crc = (prev_crc >> 8) ^ crc_table[(prev_crc ^ str[idx]) & 0xFF];
    return prev_crc ^ 0xFFFFFFFF;
}

// Conveniently create a str_const2
#define CSTRING(text) str_const2 < crc32( text ) >( text )

// Conveniently create a hana type_c<str_const2> for use in map
#define CSTRING_TYPE(text) hana::type_c<decltype(str_const2 < crc32( text ) >( text ))>

Kullanımı:

#include <boost/hana.hpp>

#include <boost/hana/map.hpp>
#include <boost/hana/pair.hpp>
#include <boost/hana/type.hpp>

namespace hana = boost::hana;

int main() {

    constexpr auto s2 = CSTRING("blah");

    constexpr auto X = hana::make_map(
        hana::make_pair(CSTRING_TYPE("aa"), 1)
    );    
    constexpr auto X2 = hana::insert(X, hana::make_pair(CSTRING_TYPE("aab"), 2));   
    constexpr auto ret = X2[(CSTRING_TYPE("aab"))];
    return ret;
}

clang-cl5.0 ile ortaya çıkan montajcı kodu :

012A1370  mov         eax,2  
012A1375  ret  

0

Ben iki çok küçük iyileştirmeler eklemek istiyorum cevap @ user1115339 arasında. Onlardan cevaba yorumlarda bahsettim, ancak kolaylık olması için buraya bir kopyala yapıştır çözümü koyacağım.

Tek fark, FIXED_CSTRING dizelerin sınıf şablonları içinde ve dizin operatörüne argümanlar olarak kullanılmasına izin veren makrodur (örneğin bir derleme zaman haritanız varsa kullanışlıdır).

Canlı örnek .

namespace  variadic_toolbox
{
    template<unsigned  count, 
        template<unsigned...> class  meta_functor, unsigned...  indices>
    struct  apply_range
    {
        typedef  typename apply_range<count-1, meta_functor, count-1, indices...>::result  result;
    };

    template<template<unsigned...> class  meta_functor, unsigned...  indices>
    struct  apply_range<0, meta_functor, indices...>
    {
        typedef  typename meta_functor<indices...>::result  result;
    };
}

namespace  compile_time
{
    template<char...  str>
    struct  string
    {
        static  constexpr  const char  chars[sizeof...(str)+1] = {str..., '\0'};
    };

    template<char...  str>
    constexpr  const char  string<str...>::chars[sizeof...(str)+1];

    template<typename  lambda_str_type>
    struct  string_builder
    {
        template<unsigned... indices>
        struct  produce
        {
            typedef  string<lambda_str_type{}.chars[indices]...>  result;
        };
    };
}

#define  CSTRING(string_literal)                                                        \
    []{                                                                                 \
        struct  constexpr_string_type { const char * chars = string_literal; };         \
        return  variadic_toolbox::apply_range<sizeof(string_literal)-1,                 \
            compile_time::string_builder<constexpr_string_type>::produce>::result{};    \
    }()


#define  FIXED_CSTRING(string_literal)                                                        \
    ([]{                                                                                 \
        struct  constexpr_string_type { const char * chars = string_literal; };         \
        return  typename variadic_toolbox::apply_range<sizeof(string_literal)-1,                 \
            compile_time::string_builder<constexpr_string_type>::template produce>::result{};    \
    }())    

struct A {

    auto test() {
        return FIXED_CSTRING("blah"); // works
        // return CSTRING("blah"); // works too
    }

    template<typename X>
    auto operator[](X) {
        return 42;
    }
};

template<typename T>
struct B {

    auto test() {       
       // return CSTRING("blah");// does not compile
       return FIXED_CSTRING("blah"); // works
    }
};

int main() {
    A a;
    //return a[CSTRING("blah")]; // fails with error: two consecutive ' [ ' shall only introduce an attribute before ' [ ' token
    return a[FIXED_CSTRING("blah")];
}

0

Benim uygulamam Boost.Hanadizeden yaklaşıma dayanıyor (değişken karakterli şablon sınıfı), ancak yalnızca C++11standart ve constexprderleme üzerinde sıkı bir kontrol sağlayan işlevleri kullanıyor (derleme zamanı ifadesi değilse derleme zamanı hatası olurdu). Fantezi yerine olağan ham C dizesinden {'a', 'b', 'c' }(bir makro aracılığıyla) oluşturulabilir.

Uygulama: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/tackle/tmpl_string.hpp

Testler: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/src/tests/unit/test_tmpl_string.cpp

Kullanım örnekleri:

const auto s0    = TACKLE_TMPL_STRING(0, "012");            // "012"
const char c1_s0 = UTILITY_CONSTEXPR_GET(s0, 1);            // '1'

const auto s1    = TACKLE_TMPL_STRING(0, "__012", 2);       // "012"
const char c1_s1 = UTILITY_CONSTEXPR_GET(s1, 1);            // '1'

const auto s2    = TACKLE_TMPL_STRING(0, "__012__", 2, 3);  // "012"
const char c1_s2 = UTILITY_CONSTEXPR_GET(s2, 1);            // '1'

// TACKLE_TMPL_STRING(0, "012") and TACKLE_TMPL_STRING(1, "012")
//   - semantically having different addresses.
//   So id can be used to generate new static array class field to store
//   a string bytes at different address.

// Can be overloaded in functions with another type to express the compiletimeness between functions:

template <uint64_t id, typename CharT, CharT... tchars>
const overload_resolution_1 & test_overload_resolution(const tackle::tmpl_basic_string<id, CharT, tchars...> &);
template <typename CharT>
const overload_resolution_2 & test_overload_resolution(const tackle::constexpr_basic_string<CharT> &);

// , where `constexpr_basic_string` is another approach which loses
//   the compiletimeness between function signature and body border,
//   because even in a `constexpr` function the compile time argument
//   looses the compiletimeness nature and becomes a runtime one.

Bir constexprişlev derleme zaman sınırı hakkındaki ayrıntılar : https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appendix-constexpr

Diğer kullanım ayrıntıları için testlere bakın.

Şu anda tüm proje deneyseldir.


0

Yardımcı makro işlevine sahip C ++ 17'de derleme zamanı dizeleri oluşturmak kolaydır:

template <char... Cs>
struct ConstexprString
{
    static constexpr int size = sizeof...( Cs );
    static constexpr char buffer[size] = { Cs... };
};

template <char... C1, char... C2>
constexpr bool operator==( const ConstexprString<C1...>& lhs, const ConstexprString<C2...>& rhs )
{
    if( lhs.size != rhs.size )
        return false;

    return std::is_same_v<std::integer_sequence<char, C1...>, std::integer_sequence<char, C2...>>;
}




template <typename F, std::size_t... Is>
constexpr auto ConstexprStringBuilder( F f, std::index_sequence<Is...> )
{
    return ConstexprString<f( Is )...>{};
}

#define CONSTEXPR_STRING( x )                                              \
  ConstexprStringBuilder( []( std::size_t i ) constexpr { return x[i]; },  \
                 std::make_index_sequence<sizeof(x)>{} )

Ve bu bir kullanım örneğidir:

auto n = CONSTEXPR_STRING( "ab" );
auto m = CONSTEXPR_STRING( "ab" );


static_assert(n == m);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.