2011-10-25 12 views
5

ठीक है, तो यह सबसे अच्छा डिज़ाइन निर्णय नहीं हो सकता है, और मैं वास्तव में कुछ ऐसा नहीं करना चाहता जैसे LuaBind ... मैं केवल उत्सुक था अगर C++ 03 में निम्नलिखित संभव है (सी ++ 11 वैरिएडिक टेम्पलेट्स के साथ इसे संभव बनाता है)। साथ ही, मुझे यकीन है कि इससे पहले पूछा गया है, लेकिन मुझे सीधा जवाब नहीं मिला!सी ++ परिवर्तनीय पैरामीटर के साथ कॉल लुआ फ़ंक्शन

मैं कोड से लुआ कार्यों कॉल करने के लिए एक सहायक विधि है कहते हैं:

void CallFunction(char* functionName, ...); 

जो संभवतः आर्ग के एन संख्या (va_arg का उपयोग कर या एक से अधिक आर्ग के किसी भी अन्य विधि)

स्वीकार कर सकते हैं मैं कैसे कर सकता है , यदि यह भी संभव है, तो प्रत्येक पैरामीटर के प्रकार को काम करें, और उचित lua_push {type}() पर पास करें; वांछित लुआ समारोह बुलाए जाने से पहले कार्य करें?

मुझे यकीन नहीं है कि यह var_arg के साथ किया जा सकता है, क्योंकि जब आप पैरामीटर को पकड़ते हैं तो आपको यह जानना होगा, मैंने इसे शून्य * से पकड़ने की कोशिश की और इसे एक विशेष टेम्पलेट में पास करने की कोशिश की, लेकिन यह कोशिश करता है इसे टेम्पलेट में पास करें।

उम्मीद है कि सी ++ में कहीं बेहतर कोई चाल या दो होगा! धन्यवाद ढेर

+0

यह संभव नहीं है। 'va_arg' मूल रूप से' शून्य * 'की एक गौरवशाली लिंक्ड-सूची है। 'शून्य *' से जुड़ी कोई अतिरिक्त प्रकार की जानकारी नहीं है। – GManNickG

+0

क्या आप वास्तव में सी या सी ++ का उपयोग कर रहे हैं। आप सी 99 के बारे में बात कर रहे हैं, लेकिन आपने इसे सी ++ के रूप में टैग किया है (शायद आप सी ++ 03 का उपयोग कर रहे हैं)? यहां तक ​​कि विविध टेम्पलेट्स के बिना, आप इससे निपटने के लिए फ़ंक्शन टेम्पलेट ओवरलोड का एक सेट प्रदान कर सकते हैं। आम तौर पर यह बहुत कठिन होगा क्योंकि पैरामीटर को आगे बढ़ाने के लिए आपको प्रत्येक पैरामीटर को कॉन्स और गैर-कॉन्स्ट संदर्भ (सी ++ 03 में) के लिए अधिभारित करना होगा, लेकिन आपके मामले में, lua_push * फ़ंक्शंस को गैर-कॉन्स्ट रेफ संस्करण की आवश्यकता नहीं होनी चाहिए, इसलिए आपको केवल एन अधिभार की आवश्यकता है (जहां एन उन अधिकतम पैरामीटर हैं जिन्हें आप समर्थन देना चाहते हैं)। –

+0

आह ये खेद है यह सी ++ 03 है, मेरी गलती। धन्यवाद, आपका सुझाव सबसे अच्छा तरीका प्रतीत होता है (मेरा एक सहयोगी इस तरह से ऐसा करता था लेकिन अगर मैं अधिक सामान्य तरीका था तो मैं उत्सुक था)। – GracelessROB

उत्तर

9

मैं कक्षा में लुआ कार्यों को कॉल करने के लिए अपनी कार्यक्षमता को लपेटने पर विचार करता हूं। इसमें कई लाभ हैं जो मैं आपको एक सेकंड में दिखाऊंगा, लेकिन पहले यहां इसके लिए एक संभावित कार्यान्वयन विचार है। कृपया ध्यान दें कि मैंने इस कोड का परीक्षण नहीं किया है (या इसे संकलित करने का भी प्रयास किया है), यह वही काम करने के मेरे पिछले प्रयासों के आधार पर मैंने अपने सिर के शीर्ष से कुछ लिखा था।

namespace detail 
{ 
    // we overload push_value instead of specializing 
    // because this way we can also push values that 
    // are implicitly convertible to one of the types 

    void push_value(lua_State *vm, lua_Integer n) 
    { 
     lua_pushinteger(vm, n); 
    } 

    void push_value(lua_State *vm, lua_Number n) 
    { 
     lua_pushnumber(vm, n); 
    } 

    void push_value(lua_State *vm, bool b) 
    { 
     lua_pushboolean(vm, b); 
    } 

    void push_value(lua_State *vm, const std::string& s) 
    { 
     lua_pushstring(vm, s.c_str()); 
    } 

    // other overloads, for stuff like userdata or C functions 

    // for extracting return values, we specialize a simple struct 
    // as overloading on return type does not work, and we only need 
    // to support a specific set of return types, as the return type 
    // of a function is always specified explicitly 

    template <typename T> 
    struct value_extractor 
    { 
    }; 

    template <> 
    struct value_extractor<lua_Integer> 
    { 
     static lua_Integer get(lua_State *vm) 
     { 
      lua_Integer val = lua_tointeger(vm, -1); 
      lua_pop(vm, 1); 
      return val; 
     } 
    }; 

    template <> 
    struct value_extractor<lua_Number> 
    { 
     static lua_Number get(lua_State *vm) 
     { 
      lua_Number val = lua_tonumber(vm, -1); 
      lua_pop(vm, 1); 
      return val; 
     } 
    }; 

    template <> 
    struct value_extractor<bool> 
    { 
     static bool get(lua_State *vm) 
     { 
      bool val = lua_toboolean(vm, -1); 
      lua_pop(vm, 1); 
      return val; 
     } 
    }; 

    template <> 
    struct value_extractor<std::string> 
    { 
     static std::string get(lua_State *vm) 
     { 
      std::string val = lua_tostring(vm, -1); 
      lua_pop(vm, 1); 
      return val; 
     } 
    }; 

    // other specializations, for stuff like userdata or C functions 
} 

// the base function wrapper class 
class lua_function_base 
{ 
public: 
    lua_function_base(lua_State *vm, const std::string& func) 
     : m_vm(vm) 
    { 
     // get the function 
     lua_getfield(m_vm, LUA_GLOBALSINDEX, func.c_str()); 
     // ensure it's a function 
     if (!lua_isfunction(m_vm, -1)) { 
      // throw an exception; you'd use your own exception class here 
      // of course, but for sake of simplicity i use runtime_error 
      lua_pop(m_vm, 1); 
      throw std::runtime_error("not a valid function"); 
     } 
     // store it in registry for later use 
     m_func = luaL_ref(m_vm, LUA_REGISTRYINDEX); 
    } 

    lua_function_base(const lua_function_base& func) 
     : m_vm(func.m_vm) 
    { 
     // copy the registry reference 
     lua_rawgeti(m_vm, LUA_REGISTRYINDEX, func.m_func); 
     m_func = luaL_ref(m_vm, LUA_REGISTRYINDEX); 
    } 

    ~lua_function_base() 
    { 
     // delete the reference from registry 
     luaL_unref(m_vm, LUA_REGISTRYINDEX, m_func); 
    } 

    lua_function_base& operator=(const lua_function_base& func) 
    { 
     if (this != &func) { 
      m_vm = func.m_vm; 
      lua_rawgeti(m_vm, LUA_REGISTRYINDEX, func.m_func); 
      m_func = luaL_ref(m_vm, LUA_REGISTRYINDEX); 
     } 
     return *this; 
    } 
private: 
    // the virtual machine and the registry reference to the function 
    lua_State *m_vm; 
    int m_func; 

    // call the function, throws an exception on error 
    void call(int args, int results) 
    { 
     // call it with no return values 
     int status = lua_pcall(m_vm, args, results, 0); 
     if (status != 0) { 
      // call failed; throw an exception 
      std::string error = lua_tostring(m_vm, -1); 
      lua_pop(m_vm, 1); 
      // in reality you'd want to use your own exception class here 
      throw std::runtime_error(error.c_str()); 
     } 
    } 
}; 

// the function wrapper class 
template <typename Ret> 
class lua_function : public lua_function_base 
{ 
public: 
    lua_function(lua_State *vm, const std::string& func) 
     : lua_function_base(vm, func) 
    { 
    } 

    Ret operator()() 
    { 
     // push the function from the registry 
     lua_rawgeti(m_vm, LUA_REGISTRYINDEX, m_func); 
     // call the function on top of the stack (throws exception on error) 
     call(0); 
     // return the value 
     return detail::value_extractor<Ret>::get(m_vm); 
    } 

    template <typename T1> 
    Ret operator()(const T1& p1) 
    { 
     lua_rawgeti(m_vm, LUA_REGISTRYINDEX, m_func); 
     // push the argument and call with 1 arg 
     detail::push_value(m_vm, p1); 
     call(1); 
     return detail::value_extractor<Ret>::get(m_vm); 
    } 

    template <typename T1, typename T2> 
    Ret operator()(const T1& p1, const T2& p2) 
    { 
     lua_rawgeti(m_vm, LUA_REGISTRYINDEX, m_func); 
     // push the arguments and call with 2 args 
     detail::push_value(m_vm, p1); 
     detail::push_value(m_vm, p2); 
     call(2); 
     return detail::value_extractor<Ret>::get(m_vm); 
    } 

    template <typename T1, typename T2, typename T3> 
    Ret operator()(const T1& p1, const T2& p2, const T3& p3) 
    { 
     lua_rawgeti(m_vm, LUA_REGISTRYINDEX, m_func); 
     detail::push_value(m_vm, p1); 
     detail::push_value(m_vm, p2); 
     detail::push_value(m_vm, p3); 
     call(3); 
     return detail::value_extractor<Ret>::get(m_vm); 
    } 

    // et cetera, provide as many overloads as you need 
}; 

// we need to specialize the function for void return type 
// as the other class would fail to compile with void as return type 
template <> 
class lua_function<void> : public lua_function_base 
{ 
public: 
    lua_function(lua_State *vm, const std::string& func) 
     : lua_function_base(vm, func) 
    { 
    } 

    void operator()() 
    { 
     lua_rawgeti(m_vm, LUA_REGISTRYINDEX, m_func); 
     call(0); 
    } 

    template <typename T1> 
    void operator()(const T1& p1) 
    { 
     lua_rawgeti(m_vm, LUA_REGISTRYINDEX, m_func); 
     detail::push_value(m_vm, p1); 
     call(1); 
    } 

    template <typename T1, typename T2> 
    void operator()(const T1& p1, const T2& p2) 
    { 
     lua_rawgeti(m_vm, LUA_REGISTRYINDEX, m_func); 
     detail::push_value(m_vm, p1); 
     detail::push_value(m_vm, p2); 
     call(2); 
    } 

    template <typename T1, typename T2, typename T3> 
    void operator()(const T1& p1, const T2& p2, const T3& p3) 
    { 
     lua_rawgeti(m_vm, LUA_REGISTRYINDEX, m_func); 
     detail::push_value(m_vm, p1); 
     detail::push_value(m_vm, p2); 
     detail::push_value(m_vm, p3); 
     call(3); 
    } 

    // et cetera, provide as many overloads as you need 
}; 

विचार है कि यहाँ निर्माण के समय में, समारोह वर्ग के नाम के साथ समारोह खोजने के लिए और रजिस्ट्री में संग्रहीत नहीं है। इस तरह से मैं इसे क्यों करता हूं, फ़ंक्शन नाम को संग्रहीत करने और इसे प्रत्येक आमंत्रण पर ग्लोबल्स इंडेक्स से प्राप्त करने के बजाय, इस तरह से अगर किसी अन्य स्क्रिप्ट के बाद के बिंदु पर वैश्विक नाम को किसी अन्य मान के साथ बदल दिया जाएगा (जो हो सकता है फ़ंक्शन से कुछ और), फ़ंक्शन ऑब्जेक्ट अभी भी सही फ़ंक्शन का संदर्भ देगा।

वैसे भी आप सोच सकते हैं कि इन सभी की परेशानी क्यों हो रही है। इस विधि के विभिन्न लाभ हैं:

अब आपके पास लुआ फ़ंक्शन ऑब्जेक्ट्स से निपटने के लिए एक स्व-निहित प्रकार है। लुआ स्टैक या लुआ आंतरिक के बारे में चिंता किए बिना, आप आसानी से अपने कोड में उन्हें पास कर सकते हैं। यह इस तरह कोड लिखने के लिए क्लीनर और कम त्रुटि-प्रवण भी है।

क्योंकि lua_function oploads op(), मूल रूप से आपके पास फ़ंक्शन ऑब्जेक्ट होता है। इसका लाभ किसी भी एल्गोरिदम या उन कार्यों को कॉलबैक के लिए कॉलबैक के रूप में उपयोग करने में सक्षम होने का लाभ है। उदाहरण के लिए मान लीजिए कि आपके पास lua_function<int> foo("foo"); है, और मान लीजिए कि लुआ में फ़ंक्शन फ़ू दो तर्क, एक डबल और एक स्ट्रिंग लेता है। अब आप यह कर सकते हैं:

// or std::function if C++11 
boost::function<int (double, std::string)> callback = foo; 
// when you call the callback, it calls the lua function foo() 
int result = callback(1.0, "hello world"); 

यह बहुत शक्तिशाली तंत्र है के रूप में आप अब अतिरिक्त आवरण कोड किसी भी प्रकार लिखने के बिना मौजूदा सी ++ कोड के लिए अपने lua कोड बाध्य कर सकते हैं।

और जैसा कि आप देख सकते हैं, यह आपको लुआ फ़ंक्शन से आसानी से वापसी मूल्य भी प्राप्त करने देता है। आपके पिछले विचार के साथ आपको CallFunction पर कॉल करने के बाद मैन्युअल रूप से स्टैक से मान निकालना होगा। यहां स्पष्ट दोष यह है कि, इस वर्ग द्वारा केवल एक वापसी मूल्य समर्थित है, लेकिन यदि आपको उससे अधिक की आवश्यकता है, तो आप आसानी से इस वर्ग के विचार पर विस्तार कर सकते हैं (यानी।आप कक्षा को कई रिटर्न प्रकारों के लिए अतिरिक्त टेम्पलेट पैरामीटर ले सकते हैं, या आप boost::any का उपयोग कर सकते हैं और उनमें से एक कंटेनर वापस कर सकते हैं)।

+0

वाह! अद्भुत जवाब, गहराई से जाने के लिए बहुत बहुत धन्यवाद। जल्द ही इसे लागू करने के लिए जाना होगा! चीयर्स! – GracelessROB

संबंधित मुद्दे