Подтвердить что ты не робот

Макрос для статического объекта std::string из литерала

Предположим, мне нужно вызвать функцию foo, которая берет ссылку const std::string из большого количества мест в моем коде:

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

Вызов функции с строковым литералом, подобным этому, создаст временные объекты std::string, каждый раз копируя литерал.

Если я ошибаюсь, компиляторы не будут оптимизировать это, создавая статический объект std::string на литерал, который может быть повторно использован для последующих вызовов. Я знаю, что g++ имеет расширенные механизмы пула пулов, но я не думаю, что он распространяется на объекты std::string.

Я могу сделать эту "оптимизацию" самостоятельно, что делает код несколько менее читаемым:

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

Используя Callgrind, я могу подтвердить, что это действительно ускоряет мою программу.

Я думал, что попытаюсь сделать макрос для этого, но я не знаю, возможно ли это. Я бы хотел что-то вроде:

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

Я попытался создать шаблон с литералом в качестве параметра шаблона, но это оказалось невозможным. А поскольку определение функции в блоке кода невозможно, у меня все из идей.

Есть ли элегантный способ сделать это, или мне нужно прибегнуть к менее читаемому решению?

4b9b3361

Ответ 1

Вы можете использовать что-то вроде создания static std::string "на месте":

#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

Живой пример

Ответ 2

Если эта функция foo не создает копию строки, то ее интерфейс является неоптимальным. Лучше изменить его, чтобы принять char const* или string_view, чтобы вызывающий объект не требовал создания std::string.

Или добавьте перегрузки:

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)); }

Ответ 3

Если вы можете использовать boost 1.55 или выше, вы можете сделать

#include <boost/utility/string_ref.hpp>

void foo(const boost::string_ref& xyz)
{
}

Ответ 5

Это будет работать для простых строк - без пробелов:

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

Использование в заголовке (разбор раз!):

DECL_STR(Foo);
DECL_STR(Bar);

В коде:

func(str_Foo);
func(str_Bar);