脚本库和函数模板

scripting library and function templating

本文关键字:函数模板 脚本      更新时间:2023-10-16

上下文

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

  1. 脚本部分:

test.ctv

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

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
}

问题

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

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

当前解决方案

让用户定义一个子函数,如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") {}
}

所以

有没有什么技巧或方法可以让我在库中更容易地使用函数/方法?(比如用编译器构建这些函数?(不要这么认为,但最好问一下)

编辑

有消息!我有自己的答案,我会把它贴出来(但有点长)(顺便说一句,英语不是我的母语,所以如果我犯了错误,我会编辑)

执行C++->脚本调用。顺便说一句,是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)

上下文细节

为了获得更多的理解(如果想重用我的解决方案),我将详细介绍我正在做的事情的上下文:

  • 有一个顶层(只能由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;
};

和子函数(一个带有无效返回,另一个带有键入返回)

/*                                                                                                                                     
* 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){}
};

现在是拆包工的工作

首先,我需要从std::vector 中打开我的arguments

  /*                                                                                                                                   
  * 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<>或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());
    }
  };

到此为止

如果你想知道更多细节,请联系我!