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

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

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

#include <utility>

namespace helper
{
  template <typename T, typename = void>
  struct is_member_of_sample : std::false_type
  {
  };

  template <typename T>
  struct is_member_of_sample<
      T,
      decltype(adl_is_member_of_sample(std::declval<T>()))> : std::true_type
  {
  };
}

namespace sample
{
  template <typename T>
  auto adl_is_member_of_sample(T && ) -> void;
}

// -- Test it

namespace sample
{
  struct X;
}

struct Y;

static_assert(helper::is_member_of_sample<sample::X>::value, "");
static_assert(not helper::is_member_of_sample<Y>::value, "");

int main(){}

Это работает отлично, пока никто не добавляет adl_is_member_of_sample в свое собственное пространство имен (или даже глобальное пространство имен). И, конечно, мне пришлось бы создать такую ​​конструкцию для каждого пространства имен, которое я хочу проверить.

Есть ли лучший способ проверить во время компиляции, если тип относится к определенному пространству имен?


Обоснование или "Зачем мне это нужно":

В EDSL я проверяю свойства типа во время компиляции, чтобы убедиться, что определенные выражения действительны или нет. Некоторые из этих черт типа довольно просты: если класс имеет using is_numeric = void, то я рассматриваю его как числовое выражение. Прекрасно работает.

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

4b9b3361

Ответ 1

Существует (специфичный для компилятора) способ проверить, находится ли тип в определенном пространстве имен, но я позволю вам решить, лучше ли это ваше или нет:

#include <utility>
#include <type_traits>

namespace helper
{
class ctstring
{
public:
  constexpr ctstring(const char* string) : _string(string)
  {
  }

  constexpr const char* c_str() const
  {
    return _string;
  }

  constexpr bool begins_with(const ctstring other) const
  {
    return !*other.c_str() ||
           (*_string && *_string == *other.c_str() &&
            ctstring(_string + 1).begins_with(other.c_str() + 1));
  }

private:
  const char* _string;
};

template <typename T>
constexpr bool is_type_in_namespace(const ctstring name)
{
#if defined(_MSC_VER)
#define PRETTY_FUNCTION_OFFSET_1 \
  (sizeof("void __cdecl helper::is_type_in_namespace<struct ") - 1)
#define PRETTY_FUNCTION_OFFSET_2 \
  (sizeof("void __cdecl helper::is_type_in_namespace<class ") - 1)

  return ctstring(__FUNCSIG__ + PRETTY_FUNCTION_OFFSET_1).begins_with(name) ||
         ctstring(__FUNCSIG__ + PRETTY_FUNCTION_OFFSET_2).begins_with(name);

#undef PRETTY_FUNCTION_OFFSET_1
#undef PRETTY_FUNCTION_OFFSET_2
#elif defined(__clang__)
  return ctstring(__PRETTY_FUNCTION__ +
                  (sizeof("bool helper::is_type_in_namespace(const "
                          "helper::ctstring) [T = ") -
                   1))
    .begins_with(name);
#elif defined(__GNUC__)
  return ctstring(__PRETTY_FUNCTION__ +
                  (sizeof("constexpr bool "
                          "helper::is_type_in_namespace(helper::ctstring) "
                          "[with T = ") -
                   1))
    .begins_with(name);
#else
#error "Your compiler is not supported, yet."
#endif
}
}

// -- Test it

namespace sample
{
struct True_X;

class True_Y;

template <typename>
class True_T;

template <typename A>
using True_U = True_T<A>;
}

struct False_X;

class False_Y;

template <typename>
class False_T;

template <typename A>
using False_U = False_T<A>;

void test1()
{
  static_assert(helper::is_type_in_namespace<sample::True_X>("sample::"), "1");
  static_assert(helper::is_type_in_namespace<sample::True_Y>("sample::"), "2");
  static_assert(helper::is_type_in_namespace<sample::True_T<int>>("sample::"), "3");
  static_assert(helper::is_type_in_namespace<sample::True_U<int>>("sample::"), "4");
  static_assert(!helper::is_type_in_namespace<False_X>("sample::"), "5");
  static_assert(!helper::is_type_in_namespace<False_Y>("sample::"), "6");
  static_assert(!helper::is_type_in_namespace<False_T<int>>("sample::"), "7");
  static_assert(!helper::is_type_in_namespace<False_U<int>>("sample::"), "8");
}

namespace sample
{
void test2()
{
  static_assert(helper::is_type_in_namespace<True_X>("sample::"), "1");
  static_assert(helper::is_type_in_namespace<True_Y>("sample::"), "2");
  static_assert(helper::is_type_in_namespace<True_T<int>>("sample::"), "3");
  static_assert(helper::is_type_in_namespace<True_U<int>>("sample::"), "4");
  static_assert(!helper::is_type_in_namespace<::False_X>("sample::"), "5");
  static_assert(!helper::is_type_in_namespace<::False_Y>("sample::"), "6");
  static_assert(!helper::is_type_in_namespace<::False_T<int>>("sample::"), "7");
  static_assert(!helper::is_type_in_namespace<::False_U<int>>("sample::"), "8");
}

namespace inner
{
void test3()
{
  static_assert(helper::is_type_in_namespace<::sample::True_X>("sample::"), "1");
  static_assert(helper::is_type_in_namespace<::sample::True_Y>("sample::"), "2");
  static_assert(helper::is_type_in_namespace<::sample::True_T<int>>("sample::"), "3");
  static_assert(helper::is_type_in_namespace<::sample::True_U<int>>("sample::"), "4");
  static_assert(!helper::is_type_in_namespace<::False_X>("sample::"), "5");
  static_assert(!helper::is_type_in_namespace<::False_Y>("sample::"), "6");
  static_assert(!helper::is_type_in_namespace<::False_T<int>>("sample::"), "7");
  static_assert(!helper::is_type_in_namespace<::False_U<int>>("sample::"), "8");
}
}
}

void test4()
{
  using namespace sample;

  static_assert(helper::is_type_in_namespace<True_X>("sample::"), "1");
  static_assert(helper::is_type_in_namespace<True_Y>("sample::"), "2");
  static_assert(helper::is_type_in_namespace<True_T<int>>("sample::"), "3");
  static_assert(helper::is_type_in_namespace<True_U<int>>("sample::"), "4");
}

int main(int argc, char* argv[])
{
  test1();
  sample::test2();
  sample::inner::test3();
  test4();
  return 0;
}

Я тестировал это для MSVC2015 и некоторого случайного онлайн-компилятора Clang и GCC 6.1.0.

Мысли:

  • Тест принимает классы и структуры из образца пространства имен и любого пространства под-имен.
  • Он не страдает от недостатков вашего решения.
  • Возможно, вы захотите создать std:: decay_t, чтобы удалить квалификаторы CV.
  • Очевидно, что код требует >= С++ 14 Изменить: не больше, С++ 11 достаточно
  • Никто не любит макросы Редактировать: удалено большинство макросов
  • Код не очень переносимый и, скорее всего, нуждается в дополнительных ветвях для определенных компиляторов и версий компилятора. Это зависит от ваших требований, если решение приемлемо.

Изменить: Реорганизованный код, чтобы сделать его более понятным и добавлена ​​поддержка GCC. Кроме того, пространство имен для проверки для теперь может быть передано как параметр

Ответ 2

К сожалению, этот метод работает только для типов без шаблонов. Для типов шаблонов ADL также проверяет пространства имен аргументов шаблона. Затем он собирает список шаблонов классов или функций (в зависимости от контекста, из которого вызывается ADL) и выбирает лучшего кандидата.

Лучшим решением было бы добавить явную проверку типам, чье членство в пространстве имен, которое вы хотите проверить. Например, вы можете выводить все типы из определенного класса или добавлять специальный член к каждому из них. Это было бы намного яснее, легче понять и поддерживать решение.

Ответ 3

std::cout << "I am " << __PRETTY_FUNCTION__ << " function." << std::endl; 

должен печатать

namespace::class::function.