确定是否传递了类型的宏

Macro that figures out if a type was passed

本文关键字:类型 是否      更新时间:2023-10-16

我想使用宏制作自己的typeid。例如,如果我打电话给MY_TYPEID(token)我希望它解析为

my_type_info<token>()它是否是一种类型,并且

my_type_info(token)它是否是一个值。

有什么办法可以在C++做到这一点吗?

这是...艰难。

这是完成的宏:

#define MY_TYPEID(...)                                          
[&](auto...) {                                              

auto &&thing(__VA_ARGS__);                              
auto probe = [](auto...) -> decltype(thing, void()) {}; 

if constexpr(detail_type_info::wizz(probe))             
return detail_type_info::my_type_info(              
std::forward<decltype(thing)>(thing)            
);                                                  
else                                                    
return detail_type_info::my_type_info<              
my_decltype(__VA_ARGS__)                        
>();                                                
}()

这。。。看起来有趣的小玩意依赖于与我的另一个答案相同的基本原理:thing是转发引用或函数声明,具体取决于参数是表达式还是类型。

thing是引用的情况很简单(ed(:它最终会作为转发的参数转发到my_type_info从那里拾取它。

thing是一个函数的情况很有趣:它有一个推导的返回类型,但尚未(也不会(定义。因此,在提供定义之前,不可能使用它。这种"使用"包括琐碎的使用,例如普通thing;:只是试图将其放入表达式中会使程序格式不正确。

此特征是通过 SFINAE 层检测的:probe是一个通用 lambda,其返回类型使用thing。但由于它是通用的,所以在我们调用lambda 之前,这实际上不会爆炸。这正是detail_type_info::wizz试图做的:

namespace detail_type_info {
template <class F>
constexpr auto wizz(F probe) -> decltype(probe(), true) { return true;  }
constexpr auto wizz(...    ) -> decltype(        false) { return false; }
}

detail_type_info::wizz(probe)尝试匹配这些重载之一。第一个重载尝试在未计算的上下文中调用probe,实例化probe(lambda(的调用运算符。如果thing确实在等待推断其返回类型,则此实例化将失败,并且整个重载都将消失。第二个重载不做这样的事情,并且始终有效,但由于...而从未优先考虑。

所以我们现在有一种方法可以通过detail_type_info::wizz(probe)来判断宏的参数是类型(false(还是表达式(true(。这是由一个if constexpr打开的,它通过使最外面的lambda成为模板而有效。

还有最后一个障碍:true分支中的detail_type_info::my_type_info(std::forward<decltype(thing)>(thing))始终有效(即使在thing函数声明的情况下如果实例化它会中断(。

然而,false分支不能以天真的方式调用return detail_type_info::my_type_info<__VA_ARGS__>(),因为当__VA_ARGS__是一个不是有效的非类型模板参数(例如double(的表达式时,这可能会变成无稽之谈,在这种情况下,编译器会立即发出咩咩声。

这就是为什么我在实现my_decltype的地方重用了我的另一个答案,它在表达式上decltype,对类型进行无操作,因此始终形成有效的函数调用。

在所有这些机器到位后,并增加了两个my_type_info存根,如下所示:

namespace detail_type_info {
template <class T>
void my_type_info(T &&) {
std::cout << __PRETTY_FUNCTION__ << 'n';
}
template <class T>
void my_type_info() {
std::cout << __PRETTY_FUNCTION__ << 'n';
}
}
int main() {
MY_TYPEID(int);
MY_TYPEID(4.2);
}

。按预期输出:

void detail_type_info::my_type_info() [with T = int]
void detail_type_info::my_type_info(T&&) [with T = double]

完整代码:

namespace detail_typeOrName {
struct probe {
template <class T>
operator T() const;
};
template <class T>
T operator * (T const &, probe);
probe operator *(probe);
}
#define my_decltype(x) decltype((x) * detail_typeOrName::probe{})
namespace detail_type_info {
template <class T>
void my_type_info(T &&) {
std::cout << __PRETTY_FUNCTION__ << 'n';
}
template <class T>
void my_type_info() {
std::cout << __PRETTY_FUNCTION__ << 'n';
}
template <class F>
constexpr auto wizz(F probe) -> decltype(probe(), true) { return true;  }
constexpr auto wizz(...    ) -> decltype(        false) { return false; }
}
#define MY_TYPEID(...)                                          
[&](auto...) {                                              

auto &&thing(__VA_ARGS__);                              
auto probe = [](auto...) -> decltype(thing, void()) {}; 

if constexpr(detail_type_info::wizz(probe))             
return detail_type_info::my_type_info(              
std::forward<decltype(thing)>(thing)            
);                                                  
else                                                    
return detail_type_info::my_type_info<              
my_decltype(__VA_ARGS__)                        
>();                                                
}()

科里鲁的现场演示