如何在 C++ 中检查标识符是否已声明?

How to check in C++ that identifier is declared?

提问人:Arty 提问时间:12/30/2020 更新时间:12/30/2020 访问量:733

问:

我想在代码的某个点检查是否声明了某个标识符,我该怎么做?x

我需要对不同类型的标识符进行这种检查 - 变量、枚举常量、函数、类型、宏等。但首先,我至少要检查变量和函数。

我需要这种检查(例如虚构),以便下一个代码可以工作,例如对于 int 变量的情况:declared(x)x

if constexpr(declared(x)) {
    int y = x + 1;
} else {
    std::cout << "Variable 'x' not declared!" << std::endl;
}

对于原因宏的情况,我可以使用,但是如何对变量/函数进行相同的检查?#ifdef x

对于全局非 lambda 函数的情况,我根据重载函数解析找出了下一个代码,但它需要使用基于辅助宏的全局定义(可以更简化吗?

在线试用!

#include <iostream>
#include <type_traits>

#define declared_func_helper(x, ...) \
    struct NotDeclared; \
    template <typename ... Args> \
    NotDeclared x(Args ...); \
    template <typename ... Args> \
    inline constexpr bool declared_func_##x(Args && ... args) { \
        return !std::is_same_v<decltype(x(args...)), NotDeclared>; \
    }

// declare some of functions
//void f(int a) {}
void g(bool b, char c) {}
    
// define helpers before or after declared functions
declared_func_helper(f);
declared_func_helper(g);

int main() {
    // check declaration of functions
    std::cout << "func 'f' declared: " << std::boolalpha << declared_func_f(int()) << std::endl;
    std::cout << "func 'g' declared: " << std::boolalpha << declared_func_g(bool(), char()) << std::endl;
}

输出:

func 'f' declared: false
func 'g' declared: true

对于非全局变量的情况,我实现了下一个代码,但它也需要通过宏定义帮助程序:

在线试用!

#include <type_traits>
#include <iostream>

#define declared_var_helper(x) \
    struct NotDeclared_##x {}; \
    NotDeclared_##x x;
#define declared_var(x) \
    ([&](){ return !std::is_same_v<decltype(x), NotDeclared_##x>; }())
    
// use helpers before variables declaration
declared_var_helper(x);
declared_var_helper(y);

int main() {
    // declare some of variables
    //bool x = false;
    int y = 0;
    // ........
    // later check declaration
    constexpr bool is_declared_x = declared_var(x), is_declared_y = declared_var(y);
    std::cout << std::boolalpha << "var 'x' declared: " << is_declared_x << std::endl;
    std::cout << "var 'y' declared: " << is_declared_y << std::endl;
}

输出:

var 'x' declared: false
var 'y' declared: true

其他情况或更简单的检查方法呢?

C++ 函数 变量 反射 声明

评论

2赞 500 - Internal Server Error 12/30/2020
我不清楚你想要什么。如果未声明,则无法成功编译引用其名称的程序。x
0赞 Sam Varshavchik 12/30/2020
这不能在 C++ 中完成。C++ 不能以这种方式工作。
0赞 Arty 12/30/2020
@500-InternalServerError如果我做两件事,可以在没有的情况下成功编译代码,首先我定义一个同名的全局变量,然后将使用代码包装到块中,请参阅我的试用版来解决此任务。基本上,因为定义了类型为 NotDeclared 的全局变量,这就是代码编译的原因,但 block 不会执行,因为没有这样的局部变量。这个局部变量的情况是我这样解决的!xif constexpr(declared(x)) { ... }
0赞 utnapistim 12/30/2020
编译器会为您检查这一点。无论哪种方式,您都应该使用声明的变量(实际上在 C++ 中没有其他方法可以做到这一点)。这看起来像是 x-y 问题的一个例子。你能添加一些关于你要完成的事情的注释吗?
1赞 500 - Internal Server Error 12/30/2020
@Arty:如果我理解正确的话,规范的方法是为基类提供一组虚拟化的默认实现。然后,客户端可以从中继承并覆盖它们想要提供自定义行为的函数。事实上,这种情况是虚拟方法存在的全部原因。如果需要,也可以以非 OO 方式使用函数指针来完成(尽管不那么优雅)。

答:

0赞 SparkyPotato 12/30/2020 #1

对于函数问题,您可以做的是声明一个全局函数指针,您的客户端必须填写该指针,并且默认情况下设置为您的函数。

例如:

const auto Function = Function_Fallback

然后到处使用。Function

评论

0赞 Arty 12/30/2020
您有一个运行时解决方案。原因,这是可能的。在运行时,你可以做任何复杂的事情。但我想要编译时解决方案。为什么?例如,我有一个非常高性能的代码部分,在该代码中,我总是只想编译一个分支 - 无论是用于定义的客户端函数还是用于回退。我负担不起额外的运行时布尔条件检查。另一个更重要的问题是,有时这些定义的函数可能是几十个或数百个不同的函数。而且这些函数可能在第三方库中,我不希望我的客户端做额外的初始化指针工作
0赞 SparkyPotato 12/30/2020
没有运行时布尔值检查。您只是使用函数指针,它与调用普通函数具有相同的开销。
0赞 Arty 12/30/2020
通过运行时解决方案,我实际上意味着我必须通过指针使用函数。想象一下,实际函数是在客户端标头中定义的一些短函数。如果我的代码直接使用此内联函数,则任何编译器都将在我的代码中内联并优化此函数调用。但是,如果指针使用函数,那么首先它不会被内联,其次将没有机会在发布时进行优化。我需要对我的高性能代码进行内联优化。inline
0赞 Arty 12/30/2020
第二件更重要的事情是,可以有数十个/数百个这样的不同函数,并且可以在第三方库中定义它们。使用额外的指针解决方案,所有客户端都必须初始化所有数百个函数的指针,即做大量的编码工作,而不仅仅是包括第三方库和编译我的代码。
0赞 utnapistim 12/30/2020 #2

这扩展了@500-internal-server-error的评论。

从您的评论中,我了解到您正在尝试将自定义点添加到您的库中。

在 OOP 中执行此操作的规范方法是为您要执行的操作定义接口(基类),并为它们添加默认实现。

然后,指定(通过文档和示例)代码的用户如何自定义此行为以满足其需求。

为此,您不需要检查客户端是否/在其代码中声明了什么。

下面是一个示例(使用一些假设类来序列化数据):

class Document // your data class, that you needd to save
{
    int a = 0; // document data (that needs to be saved)
    int b = 1;
public:
     void save() // by default, save to xml
     {
         XmlDocument store{"document.xml"};
         save(store);
     }

     void save(OutputDocument &store)
     {
         store.add("a", to_string(a));
         store.add("b", to_string(b));
     }
};

自定义点 (OutputDocument):

class OutputDocument
{
public:
     virtual void add(const std::string& name, const std::string& value) = 0;
};

默认实现 (XML):

class XmlDocument: public OutputDocument
{
public:
     void add(const std::string& name, const std::string& value) override; 
};

自定义(客户端可以执行此操作):

class JsonDocument: public OutputDocument // store to JSON instead
{
public:
     void add(const std::string& name, const std::string& value) override; 
};

客户可以说:

Document doc;
JsonDocument json{"document.json"};
doc.save(json); // save to json instead of the default (to xml)

评论

0赞 Arty 12/30/2020
为什么我不能使用接口?在这里看到我的评论。原因之一是我负担不起高性能代码中的额外虚拟级别。其次,更重要的是,数百个这样的函数可能来自某个第三方库,其中一些函数是由宏有条件定义的。我不希望我的客户端费心通过额外的虚拟接口转发所有这些数百个函数。我想直接使用第三方库函数。#ifdef ...