Macro pour std statique:: string object de literal

supposons que je doive appeler une fonction foo qui prend un const std::string de référence à partir d'un grand nombre de places dans mon code:

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

appeler une fonction avec une chaîne littérale comme celle-ci créera un des objets, de la copie de la lettre à chaque fois.

a moins que je ne me trompe, les compilateurs n'optimiseront pas cela en créant unstd::string objet par littéral qui peut être réutilisé pour des appels ultérieurs. Je sais que g++ a des mécanismes de pool de string avancés, mais je ne pensez pas que cela s'étend à la std::string les objets eux-mêmes.

je peux faire cette "optimisation" de moi-même, ce qui rend le code un peu moins lisible:

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

en utilisant Callgrind, je peux confirmer que cela accélère effectivement mon programme.

je pensais essayer de faire une macro pour ça, mais je ne sais pas si c'est possible. Ce que je veux c'est quelque chose comme:

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

j'ai essayé de créer un modèle avec le littéral comme paramètre de modèle, mais cela s'est avéré impossible. Et puisqu'une définition de fonction dans un bloc de code n'est pas possible, je suis à court d'idées.

y a-t-il une façon élégante de le faire, ou vais-je devoir recourir à la solution la moins lisible?

18
demandé sur Brian Tompsett - 汤莱恩 2014-09-09 13:09:44

5 réponses

vous pouvez utiliser quelque chose comme pour créer votre static std::string"au lieu de":

#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 ''
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<'', 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 (`` 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] : ''; }

// 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 ''
#define MAKE_CHAR_SEQUENCE(S) \
    strip_sequence<char_sequence<> \
    PUSH_BACK_128(S, 0) \
    ::push_back<check_size<128>(S) ? '' : ''> \
    >::type

// Macro to return an static std::string
#define STATIC_STRING(S) static_string<MAKE_CHAR_SEQUENCE(S)>::str

Live exemple

6
répondu Jarod42 2014-09-09 21:18:56

si cette fonction foo ne fait pas de copie de la chaîne alors son interface est sous-optimale. Il vaut mieux le changer pour l'accepter char const* ou string_view, de sorte que l'appelant n'est pas requis pour la construction std::string.

Ou ajouter des surcharges:

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)); }
10
répondu Maxim Egorushkin 2014-09-09 09:15:43

Si vous pouvez utiliser boost 1.55 ou plus, vous pouvez le faire

#include <boost/utility/string_ref.hpp>

void foo(const boost::string_ref& xyz)
{
}
2
répondu James 2014-09-09 13:16:23

Vous pouvez utiliser coup de pouce.Poids mi-mouche pour clé-valeur des poids moucheconst char*std::string. Je ne suis pas sûr des détails, pourrait être qu'il est suffisant d'utiliser flyweight<std::string> partout.

1
répondu filmor 2014-09-09 09:19:28

Cela fonctionne pour de simples chaînes - w/o espace:

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

utilisation dans l'en-tête (parse once!):

DECL_STR(Foo);
DECL_STR(Bar);

EN code:

func(str_Foo);
func(str_Bar);
1
répondu egur 2014-09-09 11:42:17