我想检查一个类型是否来自特定的命名空间。以下是我提出的建议:
#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
非常通用。其他人也可能会使用它。因此,我考虑通过检查类型是否来自预期的命名空间来支持特征。
答案 0 :(得分:3)
有一种(编译器特定的)方法来测试某个类型是否在某个命名空间中,但我会让你决定它是否比你的更好:
#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测试了这个。
思想:
编辑:重构代码以使其更清晰并添加GCC支持。此外,要测试的命名空间现在可以作为参数传递
答案 1 :(得分:-1)
不幸的是,这种技术仅适用于非模板类型。对于模板类型,ADL还会检查模板参数的名称空间。然后它收集一个类或函数模板列表(取决于调用ADL的上下文)并选择最佳候选者。
更好的解决方案是对要在其中检查名称空间的成员资格的类型添加显式检查。例如,您可以从某个类派生所有类型,或者为每个类添加一个特殊成员。这将更清晰,更容易理解和维护解决方案。
答案 2 :(得分:-3)
std::cout << "I am " << __PRETTY_FUNCTION__ << " function." << std::endl;
应该打印
namespace::class::function.