c++ macros c-preprocessor stdstring string-literals

c++ - Macro para static std:: string object from literal



macros c-preprocessor (5)

Supongamos que necesito llamar a una función foo que toma una referencia const std::string de un gran número de lugares en mi código:

int foo(const std::string&); .. foo("bar"); .. foo("baz");

Al llamar a una función con un literal de cadena como esta se crearán objetos std::string temporales, copiando el literal cada vez.

A menos que me equivoque, los compiladores no optimizarán esto al crear un objeto std::string estático por literal que puede reutilizarse para llamadas posteriores. Sé que g ++ tiene mecanismos avanzados de agrupación de cadenas, pero no creo que se extienda a los propios objetos std::string .

Puedo hacer esta "optimización" yo mismo, lo que hace que el código sea un poco menos legible:

static std::string bar_string("bar"); foo(bar_string); .. static std::string baz_string("baz"); foo(baz_string);

Usando Callgrind, puedo confirmar que esto efectivamente acelera mi programa.

Pensé que intentaría hacer una macro para esto, pero no sé si es posible. Lo que querría es algo como:

foo(STATIC_STRING("bar")); .. foo(STATIC_STRING("baz"));

Intenté crear una plantilla con el literal como parámetro de plantilla, pero eso resultó imposible. Y como la definición de una función en un bloque de código no es posible, no tengo más ideas.

¿Hay una forma elegante de hacerlo, o tendré que recurrir a una solución menos legible?


Esto funcionará para cadenas simples, sin espacios en blanco:

#define DECL_STR(s) const std::string str_##s (#s)

Uso en el encabezado (parse una vez!):

DECL_STR(Foo); DECL_STR(Bar);

En codigo:

func(str_Foo); func(str_Bar);



Puedes usar algo como para crear tu static std::string "en lugar" :

#include <cstdint> #include <string> // Sequence of char template <char...Cs> struct char_sequence { template <char C> using push_back = char_sequence<Cs..., C>; }; // Remove all chars from char_sequence from ''/0'' template <typename, char...> struct strip_sequence; template <char...Cs> struct strip_sequence<char_sequence<>, Cs...> { using type = char_sequence<Cs...>; }; template <char...Cs, char...Cs2> struct strip_sequence<char_sequence<''/0'', Cs...>, Cs2...> { using type = char_sequence<Cs2...>; }; template <char...Cs, char C, char...Cs2> struct strip_sequence<char_sequence<C, Cs...>, Cs2...> { using type = typename strip_sequence<char_sequence<Cs...>, Cs2..., C>::type; }; // struct to create a std::string template <typename chars> struct static_string; template <char...Cs> struct static_string<char_sequence<Cs...>> { static const std::string str; }; template <char...Cs> const std::string static_string<char_sequence<Cs...>>::str = {Cs...}; // helper to get the i_th character (`/0` for out of bound) template <std::size_t I, std::size_t N> constexpr char at(const char (&a)[N]) { return I < N ? a[I] : ''/0''; } // helper to check if the c-string will not be truncated template <std::size_t max_size, std::size_t N> constexpr bool check_size(const char (&)[N]) { static_assert(N <= max_size, "string too long"); return N <= max_size; } // Helper macros to build char_sequence from c-string #define PUSH_BACK_8(S, I) / ::push_back<at<(I) + 0>(S)>::push_back<at<(I) + 1>(S)> / ::push_back<at<(I) + 2>(S)>::push_back<at<(I) + 3>(S)> / ::push_back<at<(I) + 4>(S)>::push_back<at<(I) + 5>(S)> / ::push_back<at<(I) + 6>(S)>::push_back<at<(I) + 7>(S)> #define PUSH_BACK_32(S, I) / PUSH_BACK_8(S, (I) + 0) PUSH_BACK_8(S, (I) + 8) / PUSH_BACK_8(S, (I) + 16) PUSH_BACK_8(S, (I) + 24) #define PUSH_BACK_128(S, I) / PUSH_BACK_32(S, (I) + 0) PUSH_BACK_32(S, (I) + 32) / PUSH_BACK_32(S, (I) + 64) PUSH_BACK_32(S, (I) + 96) // Macro to create char_sequence from c-string (limited to 128 chars) without leading ''/0'' #define MAKE_CHAR_SEQUENCE(S) / strip_sequence<char_sequence<> / PUSH_BACK_128(S, 0) / ::push_back<check_size<128>(S) ? ''/0'' : ''/0''> / >::type // Macro to return an static std::string #define STATIC_STRING(S) static_string<MAKE_CHAR_SEQUENCE(S)>::str

Ejemplo vivo


Si esa función foo no hace una copia de la cadena, entonces su interfaz es subóptima. Es mejor cambiarlo para que acepte char const* o string_view , para que el llamante no tenga que construir std::string .

O añadir sobrecargas:

void foo(char const* str, size_t str_len); // Does real work. inline void foo(std::string const& s) { foo(s.data(), s.size()); } inline void foo(char const* s) { foo(s, strlen(s)); }


Si puedes usar boost 1.55 o mayor puedes hacerlo

#include <boost/utility/string_ref.hpp> void foo(const boost::string_ref& xyz) { }