【问题标题】:Map functions of a class while declaring the functions在声明函数时映射类的函数
【发布时间】:2010-12-20 01:42:15
【问题描述】:

我之前关于这个主题的问题得到了解答,我得到了一些运行良好的测试。 Map functions of a class

我现在的问题是,如果有办法在声明函数时能够在映射中注册它,就像我在这个关于命名空间和类的问题中所意识到的那样: Somehow register my classes in a list

命名空间和类可以使用“static”关键字在映射中注册,这样,这些静态实例将在 main() 被调用之前构建。

我可以通过类函数以某种方式做到这一点吗?
因为当我在类声明中使用 static 关键字时,我无法像在类声明之外那样初始化成员(与上面第二个 url 中的命名空间和类一样)

我想我可以硬编码构造函数中的所有成员并将它们注册到地图中,但我想知道在声明成员时是否有办法做到这一点,以便将来更容易

谢谢你,

【问题讨论】:

    标签: c++ class function map members


    【解决方案1】:

    你有什么问题?

    问题在于,不幸的是,在 C++ 中,函数不被视为一等成员。

    哦,肯定有那些指向函数的指针可以很好地工作,但是没有通用函数类型或类似的东西。

    但是有一些方法可以解决这个问题,我认为最简单的是Command 模式。

    Command 模式中,一个函数(操作)被抽象为一个对象。参数存储在对象中以供以后重用(例如undoredo 命令),并且存在一个统一的接口来执行操作本身。

    少说话,多代码:

    class Command
    {
    public:
      virtual ~Command() {}
    
      virtual Command* clone() const = 0;
    
      virtual void execute() = 0;
    };
    

    简单吗?

    class Foo {};
    
    class FooCommand: public Command
    {
    public:
      void parameters(Foo& self, int a, std::string const& b);
    
      virtual FooCommand* clone() const;
      virtual void execute();
    
    private:
      Foo* m_self;
      int m_a;
      std::string const* m_b;
    };
    

    现在,最棒的是我可以简单地将我的命令存储在地图中。

     // registration
     typedef boost::ptr_map<std::string, Command> commands_type;
    
     commands_type commands;
     commands.insert("foo", FooCommand());
    
     // get the command
     Foo foo;
     FooCommand* cFoo = dynamic_cast<FooCommand*>(commands["foo"].clone());
     if (cFoo != 0)
     {
       cFoo->parameters(foo, 2, "bar");
       cFoo->execute();
     }
    

    此提案仍需要一些工作。

    • 传递参数非常烦人,因为它需要向下转换。
    • 我并不关心异常安全,但返回 auto_ptrshared_ptr 对于 clone 方法会更好...
    • const 和非const Foo 参数之间的区别并不容易介绍。

    但是,它比使用 void* 存储指向函数的指针更安全,因为您可以使用 RTTI 检查类型是否正确。

    另一方面,现在打印链接到特定对象的命令集合非常容易(如果每个对象都有一个映射),您还可以找到模拟virtual 方法等效果的方法...

    但我希望你意识到你实际上是在尝试实现反射,这并不容易......祝你好运!

    【讨论】:

      【解决方案2】:

      您可以使用预处理器来允许以下代码:

      #include <iostream>
      
      #include "Registration.h"
      
      class myclass {
        public:
        myclass() { HANDLE_REGISTRATION(); }
      
        private:
        static void reg1()  { std::cout << "reg1" << std::endl; }
        static void reg2()  { std::cout << "reg2" << std::endl; }
        static void unreg() { std::cout << "ERROR!" << std::endl; }
      
        BEGIN_REGISTRATION();
          REGISTER(reg1);
          REGISTER(reg2);
        END_REGISTRATION();
      };
      
      int main()
      {
        myclass obj;
        obj.callAllRegistered();
      
        return 0;
      }
      

      丑陋的预处理器黑客隐藏在Registration.h

      #ifndef INCLUDED_REGISTRATION_H
      #define INCLUDED_REGISTRATION_H
      
      #include <string>
      #include <map>
      
      #define BEGIN_REGISTRATION() \
        std::map<std::string, void(*)()> reg; \
        void register_static(const std::string& name, void(*f)()) \
        { \
          reg[name] = f; \
        } \
        void registerAll() {
      
      #define REGISTER(name) register_static(#name, name)
      
      #define HANDLE_REGISTRATION() registerAll()
      
      #define END_REGISTRATION() \
        } \
        public: \
        void callAllRegistered() { \
          std::map<std::string,void(*)()>::const_iterator it; \
          for (it = reg.begin(); it != reg.end(); ++it) \
            it->second(); \
        } \
        private: \
        typedef int unusedblahblahblah___
      
      #endif
      

      【讨论】:

      • 您仍然需要手动注册所有功能。另外,在预处理器中将其组合在一起会留下许多不明显的问题,例如END_REGISTRATION 如何将当前访问级别更改为private,而不管调用之前的访问级别是什么。
      【解决方案3】:

      您正在寻求的是一个名为Reflection 的原则。不幸的是,C/C++ 不提供此功能,并且在 C++ 对象中实现它会非常复杂(如果可能的话)。

      如果需要此功能,我建议寻找另一种支持此类元编程功能的语言。在其他一些语言中,做这件事是微不足道的。例如,在 Ruby 中,您可以说:

      class Myclass
        def initialize
        end
      
        def a
        end
      
        def b
        end
      end
      
      x = Myclass.new
      x.methods
      => ["inspect", "b", "clone", "taguri", "public_methods", "display", "instance_va
      riable_defined?", "equal?", "freeze", "taguri=", "methods", "respond_to?", "dup"
      , "instance_variables", "to_yaml_style", "__id__", "method", "eql?", "id", "sing
      leton_methods", "send", "taint", "frozen?", "instance_variable_get", "__send__",
      "instance_of?", "to_a", "type", "to_yaml_properties", "protected_methods", "obj
      ect_id", "instance_eval", "==", "===", "instance_variable_set", "to_yaml", "kind
      _of?", "extend", "to_s", "a", "hash", "class", "tainted?", "=~", "private_method
      s", "nil?", "untaint", "is_a?"]
      

      这将列出与对象关联的所有成员函数(在这种情况下,其中许多是自动生成的)。对于实例变量等也可以这样做。许多其他语言都提供这些类型的功能。

      如果此功能对您的工作至关重要,那么我建议您重新检查您选择的编程语言,因为您似乎想要在比 C/C++ 通常设计的更高级别上工作。可以通过使用某种对象/类生成器模式将这种东西硬塞到 C++ 中,但编写或使用生成的类并非易事。

      【讨论】:

      • Ruby 是被解释的,对吧?我知道 C/C++ 不应该这样做,因为有像 Ruby 和 C# 这样的高级语言很容易让你反思,但我真的很喜欢 C++,我更喜欢使用它,即使它在这个主题中更难完成.
      • 我不会说这是不可能完成的,因为其他语言(如 Ruby)使用 C/C++ 构建的解释器。但是,您最终会做很多工作,因为您将朝着设计和实现一种新的编程语言迈出一大步。 Matthieu M. 有一个好主意,但正如您所见,它很快变得复杂(随着复杂性的增加,问题的可能性更大,调试难度也更大)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-20
      • 1970-01-01
      • 2023-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多