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

5
我希望在某个代码点检查某个标识符 x 是否被声明,我该如何做?
我需要这种类型的检查适用于不同类型的标识符 - 变量、枚举常量、函数、类型、宏等。但首先我至少要检查变量和函数。
我需要这样一种检查(例如想象中的 declared(x)),以便下面的代码可以正常工作,例如对于 int 变量 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

其他情况怎么办?或者有更简单的检查方法吗?


2
我不清楚你想要什么。如果x没有被声明,那么你无法成功编译一个引用了它的名称的程序。 - 500 - Internal Server Error
这在C++中是做不到的。C++不是这样工作的。 - Sam Varshavchik
@500-服务器内部错误 如果我做两件事情,首先定义一个同名的全局变量,然后将使用代码包装到“if constexpr(declared(x)) { ... }”块中,请参见我的试验来解决这个任务,那么代码可以在缺少x的情况下成功编译。基本上因为定义了类型为NotDeclared的全局变量,所以代码编译通过,但是该块不会被执行,因为没有这样的局部变量。我用这种方式解决了局部变量的问题! - Arty
编译器会为您检查这一点。无论如何,您应该使用已声明的变量(在C++中没有其他方法)。这看起来像是一个x-y问题的情况。您能否添加一些关于您想要实现什么的注释? - utnapistim
1
@Arty:如果我理解正确的话,最经典的方法是提供一个基类,并且在其中设置一系列虚拟默认实现。客户端可以从中继承并覆盖他们想要提供自定义行为的函数。实际上,这种情况也是虚方法存在的全部原因。如果需要,在非面向对象的方式下,也可以使用函数指针来完成这个过程(尽管不够优雅)。 - 500 - Internal Server Error
显示剩余2条评论
2个回答

0
你可以为函数问题声明一个全局函数指针,客户端必须填写该指针,并默认设置为你的函数。例如:
const auto Function = Function_Fallback

然后在各处使用函数


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

0

这篇文章是对@500-internal-server-error的评论的进一步扩展。

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

在面向对象编程中,实现此目的的规范方式是为您想要执行的操作定义接口(基类),并为它们添加默认实现。

然后,通过文档和示例指定如何自定义代码的用户可以调整此行为以适应其需求。

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

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

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)

为什么我不能使用接口?其中一个原因是我无法在高性能代码中承受额外的虚拟层级。更重要的第二个原因是,数百个这样的函数可能来自某些第三方库,并且其中一些函数是由 #ifdef ... 宏有条件地定义的。我不希望我的客户通过额外的虚拟接口转发所有这些数百个函数。我想直接使用第三方库函数。 - Arty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接