LINUX.ORG.RU

Как на с++11 засунуть темплейту одну и ту же строку в разных translation units?

 ,


0

1

Есть темплейт, инстанс которого надо юзать в разных файлах (например лог), и которому надо прокинуть строку конфига:

template<const char* filterLabels>
class log {
...
}

Понятно, что инстанс создается в одном месте, а в остальных объявляется через extern.

Но проблема в том, что передаваемую строку надо объявлять в хедере как inline, простого constexpr недостаточно - валится линкер. А inline поддерживается только от c++17.

// без inline - жопа
inline constexpr char labels[] = "foo,bar";

Есть ли какой-то хак для c++11, чтобы строки передавались, и темплейты понимали, что они про одно и то же (а не плодились, роняя линкер)?

Перемещено hobbit из general

★★★★★

Ничего не понял что у тебя валиться и почему (ты же не написал XD)

Но почему просто не?

static constexpr char labels[] = "foo,bar";

А, у тебя куча инстансов шаблона, в шары долблюсь))

Тогда зачем такие извраты? Используй тегирование.

struct foo_tag{};
log<foo_tag> l;

lto потом оптимизирует.

Если строка очень нужна, ее можно как статик в тег запихнуть.

PRN
()
Последнее исправление: PRN (всего исправлений: 2)

Ну, ладно, я понимаю, есть новички, которые вчера родились зарегистрировались, но ты за 15 лет не понял, что для вопросов по C++ есть Development? Потрясающе.

debugger ★★★★★
()

const char* это не строка, а указатель. Использовать указатель в качестве параметра шаблона это *уёвая идея.

iliyap ★★★★★
()
Ответ на: комментарий от PRN

https://github.com/puzrin/bt_test/blob/master/src/ring_logger/ring_logger.hpp#L32 вот исходник на всяки случай.

А как ты список тегов в темплейт передашь?

Идея в том, чтобы можно было задать фильтры, и вызовы логера с ненужными тегами превратились в пустые функции, который компилятор автоматически выпилит. Разве не будет той же проблемы, что и со строкой (надо обеспечить один и тот же указатель на данные в разных юнитах)?

Если строка очень нужна, ее можно как статик в тег запихнуть.

Да не то чтобы очень нужна. Просто я не настоящий сварщик, и ничего лучше не придумал.

Статик в теге пробовал. Та же фигня с линкером.

Vit ★★★★★
() автор топика
Последнее исправление: Vit (всего исправлений: 1)
Ответ на: комментарий от Vit

А как ты список тегов в темплейт передашь?

Списком типов. На C++17 приблизительно так:

#include <type_traits>
#include <tuple>
#include <iostream>

template <typename T, typename Tuple>
struct has_type: std::false_type {};

template <typename T, typename... Us>
struct has_type<T, std::tuple<Us...>> : std::disjunction<std::is_same<T, Us>...> {};

template <typename T, typename... Us>
inline constexpr bool has_type_v = has_type<T, Us...>::value;

struct foo_tag{};
struct bar_tag{};

struct AllGroup
{
    using List = std::tuple<foo_tag, bar_tag>;
};

struct FooGroup
{
    using List = std::tuple<foo_tag>;
};

template<class Group>
class log
{
public:
    template<typename T = foo_tag>
    std::enable_if_t<std::is_same_v<T, foo_tag> && has_type_v<foo_tag, typename Group::List>>
    foo() { std::cout << "do foo\n"; }

    template<typename T = foo_tag>
    std::enable_if_t<std::is_same_v<T, foo_tag> && !has_type_v<foo_tag, typename Group::List>>
    foo() { std::cout << "do nothing instead foo\n"; }

    template<typename T = bar_tag>
    std::enable_if_t<std::is_same_v<T, bar_tag> && has_type_v<bar_tag, typename Group::List>>
    bar() { std::cout << "do bar\n"; }

    template<typename T = bar_tag>
    std::enable_if_t<std::is_same_v<T, bar_tag> && !has_type_v<bar_tag, typename Group::List>>
    bar() { std::cout << "do nothing instead bar\n"; }
};

int main()
{
    log<FooGroup> foo;
    foo.foo();
    foo.bar();

    log<AllGroup> all;
    all.foo();
    all.bar();
}

Выведет:

do foo
do nothing instead bar
do foo
do bar

Про C++11 я забыл как про страшный сон))). По крайней мере большую часть того что выше написано на C++17, в C++11 можно заменить с помощью boost::mpl, boost::tuple и т.д.

Идея в том, чтобы можно было задать фильтры, и вызовы логера с ненужными тегами превратились в пустые функции

Еще условия в enable_if_t поменять на противоположные)

Статик в теге пробовал. Та же фигня с линкером.

Юзай наследование:

class LogFooGroup : public log<FooGroup> ...

Оно должно помочь. Дальше везде LogFooGroup вместо шаблона.

PRN
()
Последнее исправление: PRN (всего исправлений: 2)
Ответ на: комментарий от PRN

Про C++11 я забыл как про страшный сон))). По крайней мере большую часть того что выше написано на C++17, в C++11 можно заменить с помощью boost::mpl, boost::tuple и т.д.

Да я тоже не фанат C++11. Просто в esp32 и всяких странных эмбедах до сих пор 11 версия прописана. Себе я перебил на 17 уже :). Тут вопрос филосовский - смогу ли по ходу клепания проекта сделать полезную библиотеку для других, или она будет только для меня (что не желательно с моими предпочтениями).

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

Сейчас попробую компромиссный вариант - в дефолты вместо пустых строк загнать nullptr, и посмотрю проканает ли без тегов для c++11, когда лог более классический, только с log level.


Про передачу тегов как тупл типов - понял. Буду думать, готов ли я пожелать такого добра другим в качестве «простой библиотеки». В любом случае, для меня это полезно, в плане понимания как юзать плюсы «правильно». Спасибо.

Vit ★★★★★
() автор топика
Ответ на: комментарий от Vit

Проканало заменить в дефолтах пустые строки на nullptr. По умолчанию вышло совместимо с c++11, если не юзать эту фичу.

Пока оставлю так, посмотрю удобно ли в работе, может какие мысли появятся.

Vit ★★★★★
() автор топика
Ответ на: комментарий от Vit

Кстати. Ты можешь еще попробовать перенести выбор логгера в рантайм. Сделав интерфейс, а включение/выключение уже в имплементациях разными способами реализовать можно.

PRN
()
Ответ на: комментарий от PRN

Ты про #ifdef, которые ненужные вызовы похерят? Ну оно сейчас совместимо. Там подходов же не очень много как вызовы функций отключать - либо темплейты либо макросы. Других не знаю.

Vit ★★★★★
() автор топика
Ответ на: комментарий от Vit

А как ты список тегов в темплейт передашь?

А что сложного?

template<typename... Types>
struct type_list {};

...
template<
    size_t BufferSize = 10 * 1024,
    RingLoggerLevel CompileTimeLogLevel = RingLoggerLevel::DEBUG,
    size_t MaxRecordSize = 512,
    size_t MaxArgs = 10,
    typename AllowedLabels = type_list<>,
    typename IgnoredLabels = type_list<>
>
class RingLogger {
...
};
...
struct foo_label {};
struct bar_label {};
struct baz_label {};
...
using my_ring_logger = RingLogger<10*1024, RingLoggerLevel::DEBUG, 512, 10,
   type_list<foo_label, bar_label> /* AllowedLabels */,
   type_list<baz_label> /* IgnoredLabels */
>;
eao197 ★★★★★
()
Ответ на: комментарий от hobbit

Поправил.

Ты, конечно, молодец, но это не отменяет моего потрясения. Местная публика любит порассуждать о философских материях, а сама мочится мимо унитаза постит в General всё подряд без разбора. Имхо, за перенос модератором темы из одного форума в другой надо со скора штрафные очки снимать.

debugger ★★★★★
()
Последнее исправление: debugger (всего исправлений: 1)
Ответ на: комментарий от Vit

Нее я имел в виду сделать интерфейс логера и разные реализации, которые можно создавать исходя из параметров:

class log_t {
  virtual ~log_t() = default;
  virtual void foo() = 0;
  virtual void bar() = 0;
};

class log_all_t : log_t {
  void foo() override {
    // do foo
  }
  void bar() override {
    // do bar
  }
};

class log_foo_t : log_t {
  void foo() override {
    // do foo
  }
  void bar() override {
    // do nothing
  }
};

log_t& makeLog(Params params);

// И юзать можно так
auto& log = makeLog(params);
makeLog(params).foo();
PRN
()
Ответ на: комментарий от PRN

IMHO как-то слишком программингово получается. Нету ощущения простоты и удобства, на мой субъективный взгляд. Но возможно у меня профдеформации с вебни.

Vit ★★★★★
() автор топика
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.