Hiện tại tôi đang xây dựng các functors (các loại có thể gọi) cho các quy ước gọi khác nhau (__stdcall, __cdecl, __fastcall, v.v.). Với những gói tôi sẽ có thể làm điều gì đó như thế này:Các đối tượng có thể gọi với các quy ước gọi khác nhau
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;
}
Tại thời điểm này tôi đã xây dựng một wrapper cho __stdcall gọi ước rằng có thể gọi bất kỳ chức năng __stdcall miễn là các thông số chính xác được quy định và những lập luận chính xác được thông qua vào lớp trông như thế này:.
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)...);
}
};
với điều đó trong tay tôi quyết định xây dựng những gói khác nhưng tôi figured mà gõ ra cùng một mảnh mã và thay đổi quy ước gọi bên trong khai báo sử dụng cho callable_t là công việc nhiều hơn cần thiết. Vì vậy, tôi muốn tìm một cách để xây dựng khoảng 4 biến thể của các loại có thể gọi (cho mỗi quy ước gọi) nhưng không thể tìm thấy một cách để làm điều đó.
Cho đến nay tôi đã cố gắng sử dụng một enum như một tổ chức phi kiểu mẫu tham số như thế này:
template <CallingConvention Call, typename ReturnT, typename... ArgsT>
class Function
{
// ...
};
Nhưng tôi không biết làm thế nào để lặp loại đối tượng gọi và thiết lập các loại yêu cầu (I đã thử sử dụng std :: is_same/std :: enable_if nhưng đó là một kết thúc chết). Tôi cũng đã cố gắng mẫu chuyên môn với mã như thế này:
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;
};
Nhưng tôi đã không nghĩ đến việc phần còn lại của các đối số (kiểu trả về + thông số) vì vậy đây không thể làm việc quá.
Vì vậy, dù sao có bất kỳ ý tưởng nào tôi có thể làm? Một phương pháp Tôi đang nghĩ đến việc đang làm một công tắc trên thông số không loại và gọi đúng như thế này:
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...
}
}
};
Và mặc dù này trông như một giải pháp làm việc tôi đã tự hỏi nếu có là một số lựa chọn thay thế tốt mà Tôi không nghĩ đến.
Bất kỳ ý tưởng nào?
Tại sao tái tạo bánh xe? 'std :: function' đã có thể lưu trữ bất kỳ con trỏ hàm hoặc hàm functor nào, bất kể quy ước gọi, miễn là nó có thể gọi với các đối số mà bạn chỉ định. Nó không hoạt động cho bạn hay nó có một số nhược điểm cụ thể khiến bạn bắt đầu triển khai tùy chỉnh của mình? – hvd
@hvd giả sử bạn có một chuỗi std :: unordered_map ánh xạ như "glCreateProgram" và "glCreateShader" được tải từ opengl32.dll đến địa chỉ biểu tượng của chúng. Bạn muốn tạo một hàm templated function_cast mà bạn sẽ sử dụng void cho no args/returns, và std :: tuple cho args/no args, vì chúng tương thích nhị phân. như nó đứng, std: chức năng không hoạt động cho stdcall, do đó, một thời gian chạy nếu kiểm tra là cần thiết trên một số quy ước gọi điện thoại giả dựa trên đó bạn chuyển đổi gọi quy ước đúc. –
Dmitry
@Dmitry "std: chức năng không hoạt động cho stdcall" - Phiên bản cũ của GCC (lên đến GCC 5) bỏ qua quy ước gọi trong tên mangling, gây ra lỗi liên kết, nhưng điều này đã được cố định giữa năm 2015, và không nên là vấn đề với 'std :: function' đối với các quy ước gọi không mặc định kể từ đó ít nhất là trong trình biên dịch đó. Các trình biên dịch khác * nên * hoạt động theo cùng một cách. Nếu không, bạn có thể cung cấp chi tiết về cái nào không? – hvd