проверка времени компиляции на наличие уникального идентификатора - PullRequest
0 голосов
/ 04 октября 2018

Я видел много постов, объясняющих, как создать уникальный идентификатор для класса.

В моем случае идентификатор выбирается пользователем (по разным причинам), но я хочу убедиться, чтов разных классах идентификатор не используется дважды.

Я сократил свою проблему до следующего кода:

struct A {}; struct B {};

template <typename T> struct traits {};
template <> struct traits<A> { static constexpr size_t id() { return 0; }}
template <> struct traits<B> { static constexpr size_t id() { return 1; }}

Теперь есть простой способ убедиться, что кто-то не добавляетспециализация черты с дублированным идентификатором:

struct C {};
template <> struct traits<C> { static constexpr size_t id() { return 1; // this should static_assert ! }}

Я могу использовать C ++ 11, и я не хочу злоупотреблять препроцессором.

Если возможно,Решение не должно требовать ничего особенного из кода, специализирующего черту (то есть, если проверка может быть сделана извне, с учетом уже существующих специализаций, это было бы здорово).

Спасибо

Ответы [ 2 ]

0 голосов
/ 08 октября 2018

Звучит плохо и не идеально, но вы можете добиться довольно хорошей уникальности, вычисляя некоторую хеш-функцию из сцепленного макроса __LINE__ и __FILE__, определяющего:

constexpr std::uint32_t calculate_hash(std::uint32_t seed, const char data[])
{
    //some hashing algorithm
}

#define CALCULATE_HASH_FROM_LINE() calculate_hash(__LINE__, __FILE__) 

struct A {};
struct B {};
struct C {};

template <typename T> struct traits {};

template <> struct traits<A> { static constexpr size_t id() { return CALCULATE_HASH_FROM_LINE(); }}; 
template <> struct traits<B> { static constexpr size_t id() { return CALCULATE_HASH_FROM_LINE(); }};
template <> struct traits<C> { static constexpr size_t id() { return CALCULATE_HASH_FROM_LINE(); }};

Полный код: http://coliru.stacked -crooked.com / a / c1805baf9863b238

Плюсы:

  • соответствует стандарту
  • прост в использовании, потенциально трудно сломать

Минусы:

  • определение двух вещей в одной строке не удастся
  • не работает вообще с шаблонами

Другоеможно использовать макрос для извлечения имени типа:

constexpr std::uint32_t calculate_hash(std::uint32_t seed, const char data[])
{
    //some hashing algorithm
}

#define CALCULATE_HASH(T) calculate_hash(29, #T) 

struct A {};
struct B {};
struct C {};

template <typename T> struct traits {};

template <> struct traits<A> { static constexpr size_t id() { return CALCULATE_HASH(A); }}; 
template <> struct traits<B> { static constexpr size_t id() { return CALCULATE_HASH(B); }};
template <> struct traits<C> { static constexpr size_t id() { return CALCULATE_HASH(C); }};

Полный код: http://coliru.stacked -crooked.com / a / 46b00f9aea039c5b Этот метод имеет те же плюсы и минусы, что и предыдущий,но дополнительно:

Плюсы:

  • больше относится к фактическому типу, чем предыдущий метод

Минусы:

  • большеинформация для передачи в макрос
  • специализаций "черт" должна будет содержать полное пространство имен для требуемого типа (что также означает, что все специальныеzations, вероятно, должны быть размещены в одном пространстве имен).Невыполнение этого требования может привести к конфликтам имен.
  • ::A сгенерирует хеш, отличный от A

Таким образом, оба способа, вероятно, будут работать во многих случаях, но по сути неверны.C ++ очень нуждается в отражении во время компиляции: /.

0 голосов
/ 04 октября 2018

Вот одна идея, которая, я не уверен, насколько она применима для вас:

#include <cstddef>

struct A {}; struct B {}; struct C {};

template <size_t Id> constexpr size_t getId() { return Id; }

template <typename T> struct traits {};

template size_t getId<0>();
template <> struct traits<A> { static constexpr size_t id() { return getId<0>(); }};

template size_t getId<1>();
template <> struct traits<B> { static constexpr size_t id() { return getId<1>(); }};

/* This fails to compile
template size_t getId<1>();
template <> struct traits<C> { static constexpr size_t id() { return getId<1>(); }};
*/

int main() { return 0; }

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


РЕДАКТИРОВАТЬ: Как указано Oliv , вышеупомянутое решение работает, только когда все экземпляры шаблона происходят втот же переводчик.Эта версия работает с единицами компиляции, хотя она более подвержена ошибкам (параметры шаблона и возвращаемые значения должны совпадать).

#include <cstddef>

struct A {}; struct B {}; struct C {};

template <size_t Id> constexpr size_t getId();

template <typename T> struct traits {};

template <> constexpr size_t getId<0>() { return 0; }
template <> struct traits<A> { static constexpr size_t id() { return getId<0>(); }};

template <> constexpr size_t getId<1>() { return 1; }
template <> struct traits<B> { static constexpr size_t id() { return getId<1>(); }};

/* This fails to compile
template <> constexpr size_t getId<1>() { return 1; }
template <> struct traits<C> { static constexpr size_t id() { return getId<1>(); }};
*/

int main() { return 0; }

РЕДАКТИРОВАТЬ 2: Я опубликовал вопрос Почему явный шаблонинстанцирование не нарушило ODR? из-за поведения, которое я обнаружил удивительным при написании этого ответа.См. Там больше подробностей о том, что может или не может не скомпилироваться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...