Ser capaz de criar e manipular seqüências de caracteres durante o tempo de compilação em C ++ tem vários aplicativos úteis. Embora seja possível criar seqüências de tempo de compilação em C ++, o processo é muito complicado, pois a cadeia precisa ser declarada como uma sequência variável de caracteres, por exemplo,
using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;
Operações como concatenação de strings, extração de substring e muitas outras, podem ser facilmente implementadas como operações em seqüências de caracteres. É possível declarar seqüências de caracteres em tempo de compilação de forma mais conveniente? Caso contrário, existe uma proposta em andamento que permita uma declaração conveniente de seqüências de caracteres em tempo de compilação?
Por que as abordagens existentes falham
Idealmente, gostaríamos de poder declarar seqüências de caracteres em tempo de compilação da seguinte maneira:
// Approach 1
using str1 = sequence<"Hello, world!">;
ou, usando literais definidos pelo usuário,
// Approach 2
constexpr auto str2 = "Hello, world!"_s;
onde decltype(str2)
teria um constexpr
construtor. É possível implementar uma versão mais confusa da abordagem 1, aproveitando o fato de que você pode fazer o seguinte:
template <unsigned Size, const char Array[Size]>
struct foo;
No entanto, a matriz precisaria ter ligação externa; portanto, para que a abordagem 1 funcione, teríamos que escrever algo como isto:
/* Implementation of array to sequence goes here. */
constexpr const char str[] = "Hello, world!";
int main()
{
using s = string<13, str>;
return 0;
}
Escusado será dizer que isso é muito inconveniente. A abordagem 2 não é realmente possível de implementar. Se declarássemos um constexpr
operador literal ( ), como especificaríamos o tipo de retorno? Como precisamos que o operador retorne uma sequência variável de caracteres, precisamos usar o const char*
parâmetro para especificar o tipo de retorno:
constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */
Isso resulta em um erro de compilação, porque s
não é um constexpr
. Tentar contornar isso fazendo o seguinte não ajuda muito.
template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }
O padrão determina que esse formulário literal específico do operador seja reservado para tipos inteiros e de ponto flutuante. Enquanto 123_s
iria funcionar, abc_s
não iria. E se abandonarmos literais definidos pelo usuário por completo e usarmos uma constexpr
função regular ?
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */
Como antes, encontramos o problema de que a matriz, agora um parâmetro para a constexpr
função, não é mais um constexpr
tipo.
Eu acredito que deve ser possível definir uma macro do pré-processador C que usa uma string e o tamanho da string como argumentos e retorna uma sequência que consiste nos caracteres da string (using BOOST_PP_FOR
, stringification, subscritos de matriz e similares). No entanto, não tenho tempo (ou interesse suficiente) para implementar essa macro =)
fonte
constexpr
funções e inicializar matrizes (portanto, concat, substr etc.).constexpr
seqüências de caracteres podem ser analisadas durante o tempo de compilação, para que você possa seguir caminhos de código diferentes, dependendo dos resultados. Essencialmente, você pode criar EDLs no C ++; as aplicações são bastante ilimitadas.Respostas:
Não vi nada que correspondesse à elegância de Scott Schurr
str_const
apresentada no C ++ Now 2012 . Requer, noconstexpr
entanto.Veja como você pode usá-lo e o que ele pode fazer:
Não fica muito mais legal do que a verificação do intervalo de tempo de compilação!
Tanto o uso quanto a implementação estão livres de macros. E não há limite artificial para o tamanho da string. Eu postaria a implementação aqui, mas estou respeitando os direitos autorais implícitos de Scott. A implementação está em um único slide de sua apresentação, link acima.
fonte
str_const
e a outra baseada emsequence
), isso seja possível. O usuário usariastr_const
para inicializar a cadeia, mas operações subseqüentes que criam novas cadeias retornariamsequence
objetos.template<char... cs>
. Em teoria, você pode construir algo que pega uma string literal e compila o conteúdo de uma função. Veja a resposta por dyp. Uma biblioteca de aparência muito completa é metaparse . Essencialmente, você pode definir qualquer mapeamento de cadeias literais para tipos e implementá-lo com esse tipo de tecnologia.constexpr operator==
. Desculpe. A apresentação de Scott deve começar sobre como fazer isso. É muito mais fácil no C ++ 14 do que no C ++ 11. Eu nem me incomodaria em tentar em C ++ 11. Veja mais recentes de Scottconstexpr
fala aqui: youtube.com/user/CppConé possível implementar isso sem depender do impulso, usando macro muito simples e alguns dos recursos do C ++ 11:
(os dois últimos não são estritamente necessários aqui)
precisamos ser capazes de instanciar um modelo variadico com indicações fornecidas pelo usuário de 0 a N - uma ferramenta também útil, por exemplo, para expandir a tupla no argumento da função de modelo variável (consulte as perguntas: Como faço para expandir uma tupla nos argumentos da função de modelo variável?
" descompactar "uma tupla para chamar um ponteiro de função correspondente )
defina um modelo variável chamado string com o parâmetro não-tipo char:
agora a parte mais interessante - para passar literais de caracteres no modelo de string:
uma demonstração simples de concatenação mostra o uso:
https://ideone.com/8Ft2xu
fonte
operator+
vez deoperator*
?(str_hello + str_world)
CSTRING
macro. Caso contrário, você não poderá criar umaCSTRING
chamada interna para um[]
operador, pois o dobro[[
está reservado para atributos.Edit: como Howard Hinnant (e eu um pouco no meu comentário ao OP) apontou, talvez você não precise de um tipo com cada caractere único da string como um argumento de modelo único. Se você precisar disso, há uma solução sem macro abaixo.
Há um truque que encontrei ao tentar trabalhar com seqüências de caracteres em tempo de compilação. Ele requer a introdução de outro tipo além da "string de modelo", mas dentro das funções, você pode limitar o escopo desse tipo.
Ele não usa macros, mas alguns recursos do C ++ 11.
fonte
pair<int,pair<char,double>>
. Fiquei orgulhoso de mim mesmo e depois descobri essa resposta, e a biblioteca de metaparse hoje! Eu realmente deveria pesquisar SO mais detalhadamente antes de iniciar projetos tolos como este :-) Acho que, em teoria, um compilador totalmente em C ++ poderia ser construído com esse tipo de tecnologia. Qual a coisa mais louca que foi construída com isso?char[]
.my_str.print();
vez destr.print();
?Se você não quiser usar a solução Boost, poderá criar macros simples que farão algo semelhante:
O único problema é o tamanho fixo de 64 caracteres (mais zero adicional). Mas pode ser facilmente alterado dependendo de suas necessidades.
fonte
sizeof(str) > i
(em vez de anexar os0,
tokens extras )? É fácil definir umatrim
metafunção que fará isso após a macro já ter sido chamada, mas seria bom se a própria macro pudesse ser modificada.sizeof(str)
. É possível adicionar manualmente o tamanho da string,MACRO_GET_STR(6, "Hello")
mas isso exige que as macros Boost funcionem, porque a escrita manual requer 100 vezes mais código (você precisa implementar coisas simples como1+1
).Há um artigo: Usando seqüências de caracteres em metaprogramas de modelo C ++ de Abel Sinkovics e Dave Abrahams.
Ele tem alguma melhoria em relação à sua ideia de usar macro + BOOST_PP_REPEAT - não requer a passagem de tamanho explícito para a macro. Em resumo, é baseado no limite superior fixo para tamanho de string e "proteção contra sobrecarga de string":
mais aumento condicional :: mpl :: push_back .
Se você aceitar zeros à direita, loop de macro manuscrito, repetição de 2x de seqüência de caracteres na macro expandida e não tiver o Boost - então eu concordo - é melhor. No entanto, com o Boost seriam apenas três linhas:
DEMONSTRAÇÃO AO VIVO
fonte
Ninguém parece gostar da minha outra resposta: - <. Então, aqui vou mostrar como converter um str_const em um tipo real:
Compila com clang ++ -stdlib = libc ++ -std = c ++ 14 (cl 3.7)
fonte
Aqui está uma solução C ++ 14 sucinta para criar um std :: tuple <char ...> para cada sequência de caracteres em tempo de compilação passada.
E aqui está um para criar um tipo único de tempo de compilação, aparado na outra postagem de macro.
É realmente muito ruim que literais definidos pelo usuário ainda não possam ser usados para isso.
fonte
Um colega me desafiou a concatenar seqüências de caracteres na memória em tempo de compilação. Inclui instanciação de strings individuais também em tempo de compilação. A lista completa de códigos está aqui:
fonte
objdump -t a.out |grep my
não encontra nada. Quando comecei a digitar esse código, continuei experimentando a remoçãoconstexpr
das funções eobjdump
mostrei quandoconstexpr
foi omitido. Estou 99,9% confiante de que isso acontece no momento da compilação.-S
), notará que o gcc (4.7.2) realmente resolve asconstexpr
funções em tempo de compilação. No entanto, as seqüências de caracteres não são montadas em tempo de compilação. Em vez disso, (se eu interpretar corretamente) para cada caractere dessas seqüências "montadas", há umamovb
operação própria , que é sem dúvida a otimização que você estava procurando.com base na ideia de Howard Hinnant, você pode criar uma classe literal que adicionará dois literais.
fonte
str_at
vem?str_at<int I>(const char* a) { return a[i]; }
Sua abordagem nº 1 é a correta.
Não, não está correto. Isso compila com clang e gcc. Espero que seja padrão c ++ 11, mas eu não sou um linguista.
O que eu realmente adoraria no c ++ 17 seria o seguinte para ser equivalente (para concluir a abordagem nº 1)
Algo muito semelhante já existe no padrão para literais definidos pelo usuário modelados, como o void-pointer também menciona, mas apenas para dígitos. Até então, outro pequeno truque é usar o modo de edição de substituição + copiar e colar de
Se você não se importa com a macro, isso funciona (ligeiramente modificado da resposta de Yankes):
fonte
A solução da kacey para criar um tipo único de tempo de compilação pode, com pequenas modificações, também ser usada com o C ++ 11:
Usar:
fonte
Enquanto brincava com o mapa de impulso hana, me deparei com este tópico. Como nenhuma das respostas resolveu meu problema, encontrei uma solução diferente que desejo adicionar aqui, pois pode ser potencialmente útil para outras pessoas.
Meu problema era que, ao usar o mapa boost hana com seqüências hana, o compilador ainda gerava algum código de tempo de execução (veja abaixo). Obviamente, o motivo foi que, para consultar o mapa em tempo de compilação, deve ser
constexpr
. Isso não é possível, pois aBOOST_HANA_STRING
macro gera um lambda, que não pode ser usado emconstexpr
contexto. Por outro lado, o mapa precisa de strings com conteúdo diferente para serem de tipos diferentes.Como as soluções neste segmento estão usando um lambda ou não fornecendo tipos diferentes para conteúdos diferentes, achei a seguinte abordagem útil. Também evita a
str<'a', 'b', 'c'>
sintaxe hacky .A idéia básica é ter uma versão do
str_const
modelo de Scott Schurr no hash dos personagens. Éc++14
, masc++11
deve ser possível com uma implementação recursiva dacrc32
função (veja aqui ).Uso:
O código do assembler resultante com
clang-cl
5.0 é:fonte
Gostaria de adicionar duas pequenas melhorias à resposta de @ user1115339. Mencionei-os nos comentários para a resposta, mas por conveniência, colocarei aqui uma solução para copiar e colar.
A única diferença é a
FIXED_CSTRING
macro, que permite usar as strings nos modelos de classe e como argumentos para o operador de índice (útil se você tiver, por exemplo, um mapa de compilação).Exemplo ao vivo .
fonte
Minha própria implementação é baseada na abordagem da
Boost.Hana
string (classe de modelo com caracteres variados), mas utiliza apenas oC++11
padrão e asconstexpr
funções com verificação rigorosa da compilação (seria um erro de tempo de compilação, se não uma expressão de tempo de compilação). Pode ser construído a partir da sequência C bruta usual, em vez de sofisticada{'a', 'b', 'c' }
(por meio de uma macro).Implementação: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/tackle/tmpl_string.hpp
Testes: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/src/tests/unit/test_tmpl_string.cpp
Exemplos de uso:
Os detalhes sobre um
constexpr
limite de tempo de compilação de funções: https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appendix-constexprPara outros detalhes de uso, consulte os testes.
Todo o projeto atualmente é experimental.
fonte
No C ++ 17 com uma função de macro auxiliar, é fácil criar sequências de tempo de compilação:
E este é um exemplo de uso:
fonte