【问题标题】:create member function name and call it at runtime in c++在 C++ 中创建成员函数名称并在运行时调用它
【发布时间】:2013-12-21 12:22:15
【问题描述】:

谁能给我这个问题的想法。我已经在互联网上搜索过这方面的信息,但无法获得我想要的太多信息。

说有一堂课。

class Foo {
  explicit Foo() {}

  int getVar1();
  int getVar2();

  void setVar1(int v);
  void setVar2(int v);

  private:
  int var1, var2;
};

现在给出一个令牌列表 {"var1", "var2", ... "varN"},有什么方法可以在运行时创建函数名并调用 Foo 类型的某些对象的那些成员函数。喜欢例如

Foo obj;
string input = "Var1,Var2,Var3,...VarN";
vector<string> tokens = splitString(input);
for (vector<string>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) {
  string funName = "get" + *it;
  // somehow call obj.getVar1()....obj.getVarN()
}

if else 适用于少量变量,但不适用于大量变量。使用 bind 和 functors 也不能解决这个问题。一个网页建议让内存在运行时可执行,然后使用 reinterpret_cast,我不知道这是否可行。

更新

好的,从互联网上的答案和其他搜索中,我看到在 C++ 中没有优雅的方法来做到这一点。到目前为止,C++ 中还没有反射。所有 hack 都需要成员函数指针的编译时解析。 当你有很多变量和 setter 和 getter 函数时,有人可以给我关于在这些场景中的替代类设计的想法......或者 getter 和 setter 在 c++ 中是否是好的实践?

【问题讨论】:

标签: c++ boost reflection function-pointers mprotect


【解决方案1】:

您不能在运行时“添加”成员。 C++ 在编译时是强类型的。

您可以通过拥有map&lt;string, func_type&gt; 并使用它将您的字符串解析为实际函数来获得您想要的行为。您可以使用宏创建它以确保字符串名称与函数名称匹配。

#DEFINE ADD_METHOD(map_var, func) map_var["func"] = &func

【讨论】:

    【解决方案2】:

    一个简单/不完美的解决方案可能是使用中间方法检查参数并相应地调用 getVar* 方法。

    可能是这样的一个例子:

    class Foo 
    {
    public:
        explicit Foo() {}
    
        int getVar1() { return 1; }
        int getVar2() { return 2; }
    
        void setVar1(int v) { var1 = v; }
        void setVar2(int v) { var2 = v; }
    
        int callGetVar(const std::string &var)
        {
            if (var == "Var1") return getVar1();
            if (var == "Var2") return getVar2();
            else { return -1; }
        }
    
    private:
        int var1, var2;
    };
    
    int main()
    {
        Foo obj;
        std::string input = "Var1,Var2,Var3,...VarN";
        std::vector<std::string> tokens = { "Var1", "Var2", "Var2", "Var1", "Var1", "Var2", "Var2", "Var1"};
        auto tokensIT = tokens.begin();
        for (; tokensIT != tokens.end(); ++tokensIT) 
        {
            // somehow call obj.getVar1()....obj.getVarN()
            std::cout << obj.callGetVar(*tokensIT);
        }
    
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      考虑以下代码

      struct A
      {
          void f1() { std::cout << "A::f1()\n"; }
          void f2() { std::cout << "A::f2()\n"; }
          void f3() { std::cout << "A::f3()\n"; }
          void f4() { std::cout << "A::f4()\n"; }
      };
      
      std::map<std::string, void( A::* )()> m = { { "f1", &A::f1 }, { "f2", &A::f2 }, { "f3", &A::f3 }, { "f4", &A::f4 } };
      
      A a;
      
      for ( auto p : m ) ( a.*p.second )();
      

      您可以将地图作为类的数据成员。

      【讨论】:

      • 我想在我的帖子中附加您可以将地图作为类的静态数据成员
      【解决方案4】:

      为什么不以参照的方式看待它: 为每个变量分配一个索引号,从 0、1、2.... 开始 您将此值保存在地图中(键是变量名,值是分配的值)。 这些变量的所有值都保存在一个数组中,这样第一个变量的值在单元格 0 中,下一个变量在单元格 1 中,依此类推。

      所以,当您想要获取/设置值时,您需要做的就是在地图中找到它的索引,然后访问向量中的相关单元格。

      【讨论】:

        【解决方案5】:

        你可以试试this

        一个例子:

        template<class C1, class C2, class R, class... A, std::size_t... I>
        boost::json::value
        call_impl_(C1& c1, R(C2::* pmf)(A...), boost::json::array const& args,
            std::index_sequence<I...>)
        {
            return boost::json::value_from(
                (c1.*pmf)(boost::json::value_to< boost::remove_cv_ref_t<A> >(args[I])...));
        }
        
        template<class C1, class C2, class R, class... A>
        boost::json::value
        call_impl(C1& c1, R(C2::* pmf)(A...), boost::json::array const& args)
        {
            if (args.size() != sizeof...(A))
            {
                throw std::invalid_argument("Invalid number of arguments");
            }
            return call_impl_(c1, pmf, args, std::index_sequence_for<A...>());
        }
        
        template<class C>
        boost::json::value
        call(C& c, boost::string_view method, boost::json::value const& args)
        {
            using Fd = boost::describe::describe_members<C,
                boost::describe::mod_public | boost::describe::mod_function>;
            bool found = false;
            boost::json::value result;
            boost::mp11::mp_for_each<Fd>([&](auto D) {
                if (!found && method == D.name)
                {
                    result = call_impl(c, D.pointer, args.as_array());
                    found = true;
                }
                });
            if (!found)
            {
                throw std::invalid_argument("Invalid method name");
            }
            return result;
        }
        
        //test1 from https://github.com/bytemaster/boost_reflect 
        struct calculator {     //need Generic maybe..
            int add(int v, int u) { return u + v; }
            int sub(int v) { return result_ -= v; }
            int result() { return result_; }
        private:
            int result_ = 0.0;
        };
        
        BOOST_DESCRIBE_STRUCT(calculator, (), (add, sub), (result));
        
        int main(int argc, char** argv) {
            calculator cal;
            std::string line;
            std::string cmd;
            std::string args;
        
            while (true) {
                std::cerr << "Enter Method: ";
                std::getline(std::cin, line);
                int pos = line.find('(');
                cmd = line.substr(0, pos);
                args = line.substr(pos + 1, line.size() - pos - 2);
                std::cout << "args: " << args << std::endl;
                std::vector<std::string> num_str;
                boost::split(num_str, args, boost::is_any_of(","));
                std::vector<int> nums;
                std::for_each(num_str.begin(), num_str.end(), [&](std::string str) {nums.push_back(std::stoi(str)); });
                // Convert the vector to a JSON array
                const boost::json::value jv = boost::json::value_from(nums);
                std::cout << call(cal, cmd, jv) << std::endl;
            }
            return 0;
        }
        

        在visual studio 2022 c++17下可以通过。 用cpp20会报错,不知道为什么

        【讨论】:

          猜你喜欢
          • 2017-04-27
          • 1970-01-01
          • 1970-01-01
          • 2013-12-14
          • 2012-07-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多