脚本库和函数模板

时间:2014-10-02 10:08:55

标签: c++ templates c++11 variadic-templates

上下文

我目前正在使用自己的库来加载c ++应用程序中的自定义脚本。 这里有一些示例代码,用于解释它正在做什么:

  1. 脚本部分:
  2. test.ctv

    script
    {
      object player = access("player");
      player.posX = 1 + 2;
      access("map").load("map.txt");
    }
    
    1. C ++部分:
    2. TEST.CPP

      class Player : public Loadable{
      private:
       friend class cTVScript::scriptExecutor;
       primaryLoadable<int> posX;
       stringLoadable name;
      public:
       Player() : Loadable(&posX, "posX", &name, "name");
      }
      
      class Map : public Loadable{
      private:
       friend class cTVScript::scriptExecutor;
       std::string mapName;
      
      public:
       void load(std::string map) {
        mapName = map;
       }
       Map() : Loadable(&load, "load") {}
      }
      
      int main() {
       Player *p = new Player();
       Map *m = new Map();
       cTVScript::LoadScript("test.ctv");
       cTVScript::AddObject(p, "player");
       cTVScript::AddObject(m, "map");
       std::cout << player.posX.get() << std::endl; // for example purpose we just assume that posX are public
       std::cout << player.mapName.get() << std::endl; // same for mapName
      }
      

      问题

      cTVScript::scriptExecutor的变量访问和使用非常简单, 但我的主要问题是在其他地方:

      • 在c ++中,如何使用不同原型保存和调用方法/函数?
      • 编译器的一些技巧可以让它变得更容易吗? (比如知道参数的类型和数量?)

      目前的解决方案

      让用户定义像AccessibleLoad

      这样的子功能
      class Map{
       [...]
       public:
       void load(std::string map) {
        mapName = map;
       }
       static void AccessibleLoad(cTVScript::CallingPack& pack) {
        /* arguments */
        std::string map;
        pack.loadArguments(map); // here the user ask for each arguments
      
        /*calling object */
        Map* _this;
        pack.loadCallingObject(_this); // and here he ask for the calling object
      
        _this->load(map);
       }
       Map() : Loadable(&AccessibleLoad, "load") {}
      }
      

      所以!

      是否有一种技巧或某种方式可以让我更容易在我的库中使用函数/方法? (比如用编译器构造这些函数?(不要这么想但最好问))

      修改

      有新闻!我得到了自己的答案,我会发布它(但它有点长) (顺便说一句,英语不是我的母语,所以如果我犯了错误,请说我,我会编辑)

2 个答案:

答案 0 :(得分:1)

做C ++ - &gt;你的脚本调用。顺便说一下,这是c ++ 11

您需要某种形式的打包器,可以输入类型并添加。

class SomeClassYouCanCallAScriptFunction {
    // the order of these templates matter or else
    // the one bellow will not be able to find the one higher

    template<class T, class... Args>
    callFunction(string name){
         // run your code to call your scripted function
         // arguments has the arguments array
    }

    template<class T, class... Args>
    callFunction(string name, T var){
        // last one
        // either this
        arguments.pack<T>(var);
        // or this
        arguments.pack(to_string(var));
        // or the like
        // now do the next one
        callFunction(name);
    }



    template<class T, class... Args>
    callFunction(string name, T var, Args... args){
        // either this
        arguments.pack<T>(var);
        // or this
        arguments.pack(to_string(var));
        // or the like
        // now do the next one
        callFunction(name, args...);
    }







}


someClass.callFunction("scriptFunc", "ya", 42, someVectMaybe);

除此之外,您可以做的最好的事情是提供arguments变量并让用户获得传递的参数,如arguments.get<T>(index)

答案 1 :(得分:0)

上下文详细信息

如需更多理解(如果想重新使用我的解决方案),我会详细介绍我正在做的事情:

  • 有一个顶级(只能由cTVScript::Executor访问)
  • 低级别(用户可见(或几乎))

顶级(或非打字部分)

/*                                                                                                                                     
* Non-Typed Part                                                                                                                       
*/
class Loadable{
public:
  virtual std::string getAsString() { return ""; }
};

struct parametersPack{
public:
  Loadable* returnValue;
  std::vector<Loadable*> arguments;
};

低级别(或打字部分)

class StringLoadable : public Loadable{
private:
  std::string value;
public:
  StringLoadable(std::string _s) : value(_s) {}
  virtual std::string getAsString() { return value; }
  virtual std::string get() { return value; }
  virtual std::string& getRef() { return value; }
};

template<typename type>
class primaryLoadable : public Loadable{
private:
  type value;
public:
  primaryLoadable(type _v) : value(_v) {}
  virtual std::string getAsString() { return std::to_string(value); }
  type get() {return value;}
  type& getRef() {return value;}
};

使用不匹配的原型保存函数

父函数类(用于存储它们):

class functionLoadable : public Loadable{
public:
  virtual void call(parametersPack& pack) = 0;
};

和子功能(一个带有void返回,另一个带有typed-Return)

/*                                                                                                                                     
* Static Loadable Function                                                                                                             
*/
template <typename Return, typename... Arguments>
class StaticLoadableFunction : public functionLoadable{
private:
  Return (*fn)(Arguments...);
public:
  Return calling(Arguments... args) {
    Return value = fn(args...);
    return (value);
  }
  virtual void call(parametersPack& pack) {
    Unpacker::applyFunc(pack.arguments, fn);
  }
  StaticLoadableFunction(Return (*_fn)(Arguments...)) : fn(_fn){}
};

template <typename... Arguments>
class StaticLoadableFunction<void, Arguments...> : public functionLoadable{
private:
  void (*fn)(Arguments...);
public:
  void calling(Arguments... args) {
    fn(args...);
  }
  virtual void call(parametersPack& pack) {
    Unpacker::applyFunc(pack.arguments, fn);
  }
  StaticLoadableFunction(void (*_fn)(Arguments...)) : fn(_fn){}
};

现在解包器的工作

首先,我需要从我的arguments

中解压缩std::vector
  /*                                                                                                                                   
  * Unpacking all arguments                                                                                                            
  */
  template<unsigned int N>
  struct helper;

  template<unsigned int N>
  struct helper{
    template <typename ReturnType, typename... Arguments, typename ...final>
    static ReturnType applyFunc(std::vector<Loadable*> parameters, ReturnType (*fn)(Arguments...), final&&... args) {
      return (helper<N - 1>::applyFunc
              (parameters, fn,
               convertLoadableTo< typename parametersType<N - 1, Arguments...>::type >
               ::transform(parameters[N-1]),
               args...));
    }
  };

  template<>
  struct helper<0>{
    template <typename ReturnType, typename ...Arguments, typename ...final>
    static ReturnType applyFunc(std::vector<Loadable*> parameters, ReturnType (*fn)(Arguments...), final&&... args) {
      return (fn( args... ));
    }
  };

  template <typename ReturnType, typename ...Arguments>
  ReturnType applyFunc(std::vector<Loadable*> args, ReturnType (*fn)(Arguments...)) {
    return (helper<sizeof...(Arguments)>::applyFunc(args, fn));
  }

我知道想要知道每次递归中的类型:

  /*                                                                                                                                   
  * Getting Parameters type N in variadic Templates                                                                                    
  */
  template <int N, typename... T>
  struct parametersType;

  template <typename T0, typename... T>
  struct parametersType<0, T0, T...> {
    typedef T0 type;
  };
  template <int N, typename T0, typename... T>
  struct parametersType<N, T0, T...> {
    typedef typename parametersType<N-1, T...>::type type;
  };

然后将我的Loadable*对象向下转换为primaryLoadable&lt;&gt;或StringLoadable

  /*                                                                                                                                   
  * Treat For Each Type                                                                                                                
  */
  template <typename arg>
  struct convertLoadableTo;

  template <typename arg>
  struct convertLoadableTo{ // int, double...etc                                                                                       
    static arg transform(Loadable* l) {
      primaryLoadable<arg>* _l =
        dynamic_cast< primaryLoadable<arg>* >(l);
      if (!_l)
        throw;
      return (_l->get());
    }
  };

  template <typename arg>
  struct convertLoadableTo<arg&>{ // int&, double&...etc                                                                               
    static arg& transform(Loadable* l) {
      primaryLoadable<arg>* _l =
        dynamic_cast< primaryLoadable<arg>* >(l);
      if (!_l)
        throw;
      return (_l->getRef());
    }
  };

  template <>
  struct convertLoadableTo<std::string>{ // int&, double&...etc                                                                        
    static std::string transform(Loadable* l) {
      StringLoadable* _l =
        dynamic_cast< StringLoadable* >(l);
      if (!_l)
        throw;
      return (_l->get());
    }
  };

  template <>
  struct convertLoadableTo<std::string&>{ // int&, double&...etc                                                                       
    static std::string& transform(Loadable* l) {
      StringLoadable* _l =
        dynamic_cast< StringLoadable* >(l);
      if (!_l)
        throw;
      return (_l->getRef());
    }
  };

那就结束了!

如果您想了解更多细节,请加油!