2015-12-29 15 views
8

फिलहाल मैं विभिन्न कॉलिंग सम्मेलनों (__stdcall, __cdecl, __fastcall इत्यादि) के लिए फ़ैक्टर (कॉल करने योग्य प्रकार) बना रहा हूं। के रूप में सही पैरामीटर निर्दिष्ट कर रहे हैं मैं __stdcall बुला परंपरा है कि जब तक किसी भी __stdcall फ़ंक्शन को कॉल करने में सक्षम है के लिए एक आवरण का निर्माण किया हैविभिन्न कॉलिंग सम्मेलनों के साथ कॉल करने योग्य ऑब्जेक्ट्स

void __stdcall foo(int arg) 
{ 
    std::printf("arg: %i\n", arg); 
} 

int main(int, char**) 
{ 
    Function<void, int> v{foo}; 
    v(1337); 

    return EXIT_SUCCESS; 
} 

फिलहाल: रैपर के साथ मैं इस तरह कुछ करने के लिए सक्षम हो जाएगा और सही तर्क में पारित कर रहे हैं वर्ग इस तरह दिखता है:।

template <typename ReturnT, typename... Args> 
class Function 
{ 
    // NOTE: This version of my callable types 
    // only supports the __stdcall calling 
    // convention. I need support for __cdecl, 
    // __fastcall and also __thiscall. 
    using return_t = ReturnT; 
    using callable_t = return_t(__stdcall*)(Args...); 

private: 
    callable_t mCallable; 

public: 
    template <typename FuncT> 
    Function(FuncT const &func) : 
     mCallable(func) 
    { 
     ; 
    } 

    void operator()(Args&&... args) 
    { 
     mCallable(std::forward<Args>(args)...); 
    } 
}; 
कि हाथ में मैं अन्य रैपर बनाने का निर्णय लिया साथ

लेकिन मैं लगा कि कोड के एक ही हिस्से को लिखकर और के अंदर बुला सम्मेलन को बदलने कॉल करने योग्य_टी के लिए उपयोग की घोषणा आवश्यकतानुसार अधिक काम है। तो मैं कॉल करने योग्य प्रकारों के बारे में 4 प्रकार (प्रत्येक कॉलिंग सम्मेलन के लिए) बनाने का एक तरीका खोजना चाहता था लेकिन इसे करने का कोई तरीका नहीं मिला।

अब तक मैं इस तरह एक गैर प्रकार टेम्पलेट पैरामीटर के रूप में एक enum का उपयोग करने की कोशिश की है:

template <CallingConvention Call, typename ReturnT, typename... ArgsT> 
class Function 
{ 
    // ... 
}; 

लेकिन मैं कॉल वस्तु के प्रकार पुनरावृति और आवश्यक प्रकार की स्थापना के लिए पता नहीं कैसे (मैं std :: is_same/std :: enable_if का उपयोग करने का प्रयास किया लेकिन यह एक मृत अंत था)। मैं भी इस तरह के कोड के साथ टेम्पलेट विशेषज्ञता की कोशिश की:

struct StdcallT { ; }; 
struct CdeclT { ; }; 
struct FastcallT { ; }; 

template <typename CallT> 
struct BaseT { }; 

template <> struct BaseT<StdcallT> { using CallableT = void(__stdcall*)(); }; 
template <> struct BaseT<CdeclT> { using CallableT = void(__cdecl*)(); }; 
template <> struct BaseT<FastcallT> { using CallableT = void(__fastcall*)(); }; 

template <typename CallT> 
class Function 
{ 
    using CallableT = typename BaseT<CallT>::CallableT; 
}; 

लेकिन मैं तर्क के बाकी (वापसी प्रकार + पैरामीटर) के बारे में सोच नहीं किया गया था तो यह भी काम नहीं कर सकता।

तो क्या कोई विचार है कि मैं क्या कर सकता हूं? एक विधि मैं सोच रहा हूँ की गैर प्रकार पैरामीटर पर एक स्विच कर रहे हैं और इस तरह से सही बुला रहा है:

template <CallingConvention Call, typename ReturnT, typename... ArgsT> 
class Function 
{ 
    void operator()(ArgsT&&... args) 
    { 
     switch(Call) 
     { 
      case CallingConvention::Cdecl: 
       // Call a __cdecl version 
       break; 
      case CallingConvention::Stdcall: 
       // Call an __stdcall version 
       break; 
      // And so on... 
     } 
    } 
}; 

और इस के बावजूद एक काम कर समाधान अगर वहाँ था कुछ अच्छे विकल्प मैं सोच रहा था की तरह लग रही है कि मैं सोच नहीं रहा हूँ।

कोई विचार?

+0

पहिया को फिर से क्यों शुरू करें? 'std :: function' पहले से ही किसी भी फ़ंक्शन पॉइंटर या फ़ैक्टर को स्टोर करने में सक्षम होना चाहिए, भले ही कॉलिंग कन्वेंशन के बावजूद, जब तक यह आपके द्वारा निर्दिष्ट तर्कों के साथ कॉल करने योग्य हो। क्या यह आपके लिए काम नहीं करता है, या क्या इसमें कुछ ठोस कमीएं हैं जो आपको अपने कस्टम कार्यान्वयन पर शुरू कर रही हैं? – hvd

+0

@hvd मान लीजिए कि आपके पास std :: unordered_map मैपिंग स्ट्रिंग्स जैसे "glCreateProgram" और "glCreateShader" opengl32.dll से उनके प्रतीक पते पर लोड किए गए हैं। आप एक टेम्पलेटेड फ़ंक्शन function_cast <कॉलिंग कन्वेंशन, आउटपुट टाइप, इनपुट टाइप> बनाना चाहते हैं कि आप कोई तर्क/रिटर्न के लिए शून्य का उपयोग नहीं करेंगे, और std :: tuple args/no args के लिए, क्योंकि वे द्विआधारी संगत हैं। जैसा कि यह खड़ा है, std: फ़ंक्शन stdcall के लिए काम नहीं करता है, इसलिए एक रनटाइम अगर कुछ डमी कॉलिंग सम्मेलन पर चेक की आवश्यकता होती है जिसके आधार पर आप कॉलिंग कन्वेंशन कास्टिंग स्विच करते हैं। – Dmitry

+0

@Dmitry "std: function stdcall के लिए काम नहीं करता है" - जीसीसी के पुराने संस्करण (जीसीसी 5 तक) ने नामांकन में कॉलिंग सम्मेलन को अनदेखा कर दिया, जिससे लिंकर त्रुटियां आईं, लेकिन यह मध्य 2015 के मध्य में तय की गई थी, और वहां नहीं होना चाहिए तब से कम से कम उस कंपाइलर में गैर-डिफ़ॉल्ट कॉलिंग सम्मेलनों के लिए 'std :: function' के साथ समस्याएं हो। अन्य कंपाइलर्स * एक ही तरीके से काम करना चाहिए। यदि कोई नहीं करता है, तो क्या आप विवरण दे सकते हैं कि यह कौन सा है? – hvd

उत्तर

1

यदि आप अभी भी गणना किए गए टेम्पलेट तर्क का उपयोग करना चाहते हैं, तो आप इसे पूरा करने के लिए विशेषज्ञता का उपयोग कर सकते हैं।

enum CallingConvention { __stdcall, ... }; 

template < CallingConvention Call > 
struct implement { 
    template</* Template arguments for call method */> 
    static ReturnT call(/* arguments to run method */); 
}; 

template < CallingConvention Call, typename ReturnT, typename... ArgsT > 
class Function 
{ 
    // ... 
    template <typename FuncT> 
    Function(FuncT const &func) : mCallable(func), mCall(Call) {} 
    CallingConvention const mCall; 

    return_t operator()(ArgsT&&... args) { 
     return implement<Call>::call</* Template arguments for call method */>(/* arguments to run method */); 
    }; 
}; 

template < > 
struct implement<__stdcall> { 
    template</* Template arguments for call method */> 
    static ReturnT call(/* arguments to run method */) { 
     // Special implementation... 
    } 
}; 

यह एक स्विच स्टेटमेंट से बेहतर होगा।

(टेम्पलेट तर्क के लिए टिप्पणियों के बारे में खेद है कि मैं कि कैसे काम करता है के साथ काफी परिचित नहीं हूँ)

वह स्थान है जहां मैं के लिए क्या मैं did विचार आया।


आशा है कि इससे मदद मिलती है!

1

खैर एक बार आप प्रत्येक बुला सम्मेलन के लिए टैग परिभाषित करते हैं, आप नियमित रूप से टैग प्रेषण का उपयोग कर सकते हैं:

#include <iostream> 
#include <type_traits> 

struct cdecl_tag { typedef void (__attribute__((cdecl)) *type)(); }; 
struct stdcall_tag { typedef void (__attribute__((stdcall)) *type)(); }; 
struct fastcall_tag { typedef void (__attribute__((fastcall)) *type)(); }; 

constexpr void get_func_calling_convention_tag() {}; 

template<typename R, typename... Args> 
constexpr cdecl_tag 
get_func_calling_convention_tag (R (__attribute__((cdecl)) *)(Args...)) 
{ return {}; } 

template<typename R, typename... Args> 
constexpr stdcall_tag 
get_func_calling_convention_tag (R (__attribute__((stdcall)) *)(Args...)) 
{ return {}; } 

template<typename R, typename... Args> 
constexpr fastcall_tag 
get_func_calling_convention_tag (R (__attribute__((fastcall)) *)(Args...)) 
{ return {}; } 

#define CALLING_CONVENTION_TAG(func) \ 
decltype(get_func_calling_convention_tag(&func)) 

int __attribute__((cdecl)) foo (char) { return 0; } 
long __attribute__((stdcall)) bar (int) { return 0; } 

int main() 
{ 
    std::cout << std::is_same<CALLING_CONVENTION_TAG(foo), 
           cdecl_tag>::value     << '\n' 
       << std::is_same<CALLING_CONVENTION_TAG(bar), 
           stdcall_tag>::value     << '\n' 
       << std::is_same<CALLING_CONVENTION_TAG(foo), 
           CALLING_CONVENTION_TAG(bar)>::value << std::endl; 

    return 0; 
} 

यह देखें कार्रवाई में: http://ideone.com/HSZztX
इस कोर्स के आगे विकसित किया जा सकता; टैग में एक रिबिंड वैरैडिक सदस्य टेम्पलेट हो सकता है जो निर्दिष्ट कॉलिंग कॉन्फ़्रेंस के साथ एक फ़ंक्शन पॉइंटर प्रकार देता है।

मुझे लगता है कि आप एक मैक्रो में अच्छी तरह से टैग परिभाषाओं को कॉपी करके चिपकाने और पेस्टिंग को भी कम कर सकते हैं।

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