【问题标题】:C++ code generation: create a factory for function pointersC++ 代码生成:为函数指针创建工厂
【发布时间】:2014-08-25 14:57:11
【问题描述】:

我有一个长期且稳定增长的(非成员)函数列表,我需要在运行时从该列表中选择一个函数(基于命令行参数)。目前,我使用一个工厂函数来执行此操作,该函数接受一个字符串(函数的名称)并返回一个指向函数的指针。然而,这意味着我每次添加新函数时都必须编辑工厂函数(这既烦人又违反了DRY principle)。

我想通过解析函数列表的源代码以某种方式生成工厂函数(灵感来自昨晚阅读The Pragmatic Programmer 的代码生成部分)。然而,正确解析 C++ 似乎非常困难,我宁愿不让我的项目仅仅为此而依赖 libclang。

所以我的问题是:如何在运行时按名称选择函数而不引入重量级依赖项?

仅部分解析 c++ 代码是否“安全”(例如,仅使用正则表达式通过匹配看起来像一组函数参数的内容来提取函数名称)?

是否有任何其他(便携且不太老套)无需手动编写选择代码的方式来做到这一点?

编辑:忘了说:我不允许使用 C++11,所以很遗憾没有 lambda 函数。

【问题讨论】:

  • 所有函数都具有相同的签名吗?
  • 使用map<string,void*>(或funcptr typedef)来存储名称和地址。
  • @Quentin 是的,所以提取函数名称不会太难......但这是个好主意吗?
  • @SHR 这比我现在做的要好。但是,它仍然需要在多个地方写出函数名称,对吧?
  • 您可以看看 google-gtest 或其他框架如何在链接时使用自动类注册(即stackoverflow.com/q/9975672/847349stackoverflow.com/q/13824375/847349)。问题在于 MSVC 中的链接器优化会抛出无法从 main 访问的类型。

标签: c++ function-pointers c++03


【解决方案1】:

这是一个完整的示例:

#include <iostream>
#include <map>

namespace detail {
    struct FuncPtrMap {
        friend struct FuncPtrRegisterer;
        using FuncPtr = void(*)();
        static std::map<std::string, FuncPtr> const &getMap() {
            return getWritableMap();
        }

    private:
        // SIOF-proof singleton
        static std::map<std::string, FuncPtr> &getWritableMap() {
            static std::map<std::string, FuncPtr> theMap;
            return theMap;
        }
    };

    // Each static instance will register one function pointer
    struct FuncPtrRegisterer {
        FuncPtrRegisterer(FuncPtrMap::FuncPtr funcPtr, char const *funcName) {
            FuncPtrMap::getWritableMap().emplace(funcName, funcPtr);
        }
    };
}

// Public access to the function pointers map
auto const &gFunctionPointersMap = detail::FuncPtrMap::getMap();

#define DO_CAT(A, B) A##B
#define CAT(A, B) DO_CAT(A, B)

// Registering macro : defines a static registerer
// with a (hopefully) unique name.
#define REGISTER_FUNC(NAME) detail::FuncPtrRegisterer \
    CAT(fpreg, __COUNTER__) (&NAME, #NAME)


//
// Test
//

void myFunc1() {
    std::cout << "func1\n";
}
REGISTER_FUNC(myFunc1);

void myFunc2() {
    std::cout << "func2\n";
}
REGISTER_FUNC(myFunc2);

int main()
{
    for(auto const &kv : gFunctionPointersMap) {
        std::cout << "Calling " << kv.first << " : ";
        kv.second();
    }

    return 0;
}

打印:

Calling myFunc1 : func1
Calling myFunc2 : func2

只需在您要注册的每个功能后添加REGISTER_FUNC(func)。您只需要函数的声明,而不是它的定义。请注意,这在头文件中不起作用。

之后,您可以从main 的开头随时访问gFunctionPointersMap :)

编辑:C++03 版本here(实际上没有太大变化)。

【讨论】:

  • 很好,虽然我不认为__COUNTER__ (技术上)在标准中
  • 不是。如果它没有在特定的编译器上实现,可以用__LINE__ 替换,差别不大,但我会坚持语义。
  • 那么宏的每个实例都会生成一个新结构,它(在它的构造函数中)将函数添加到地图中? fpreg 是做什么的?
  • 宏的每个实例都会生成一个FuncPtrRegisterer 结构的实例。由于__COUNTER__,实例被命名为fpreg0fpreg1fpreg2等等。您当然可以将“fpreg”更改为您喜欢的不会发生冲突的任何内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-25
  • 1970-01-01
  • 2018-09-12
  • 2021-02-14
  • 1970-01-01
相关资源
最近更新 更多