標籤:

static_cast<const char*>("Fuck GTK+")

今天 Mili (zhihu.com/people/milizh)同學繼續在做GacUI(gaclib.net/)的移植工作,結果因為傻逼GTK+是C語言寫的,Signal都是內部亂cast的,但是GacUI的OS Driver又都是介面,於是就遇到了一個被無數人解決了無數遍的問題:如何把類成員函數指針變成void(*)(void)從而進行回調

前輩們的代碼肯定是不能抄的,畢竟現在已經是C++17了,當然要用現代的方法來解決。於是我就展示了下面的這個解決方案以供參考。

整份代碼的需求在最下面,已知Fuck接受回掉函數,已知Callback::Invoke就是那個回掉函數,因此上面的一個片段就是構造了一個CALLBACK宏,把Callback*和&Callback::Invoke變成了void(*)(void)和void*。

原理很簡單,就是不斷的用模板元編程來日clang++,然後祈禱clang++不會被日死。不過中間還是遇到了一些小問題,譬如說CallbackWrapper偏特化的時候,前面的template聲明裡面用了using後的void(T::*)(TArgs...),因為不想寫兩遍。VC++就可以把using的東西展開進去繼續推導,clang++直接就罷工了。真是爛爆了。

#include <iostream>#include <type_traits>using namespace std;/******************************************************************CALLBACK******************************************************************/template<typename T>using ObjectOf = typename std::remove_pointer< typename std::remove_reference<T>::type >::type;template<typename T, T f>struct CallbackWrapper{ static void Get() { }};template<typename T, typename ...TArgs, void(T::*f)(TArgs...)>struct CallbackWrapper<void(T::*)(TArgs...), f>{ static void Invoke(TArgs ...args, void* argument) { (reinterpret_cast<T*>(argument)->*f)(args...); } static void(*Get())() { return reinterpret_cast<void(*)()>(&Invoke); }};#define CALLBACK(OBJECT, NAME) CallbackWrapper< decltype(&ObjectOf<decltype(OBJECT)>::NAME), &ObjectOf<decltype(OBJECT)>::NAME >::Get(), static_cast<void*>(OBJECT)/******************************************************************CLIENT******************************************************************/void Fuck(void(*shit)(), void* argument){ auto bitch = reinterpret_cast<void(*)(int, double, const char*, void*)>(shit); bitch(1, 1.2, "Fuck GTK+", argument);}struct Callback{ int context; void Invoke(int a, double b, const char* c) { cout << context << "," << a << "," << b << "," << c << endl; }};int main(){ Callback callback{ 200 }; Fuck(CALLBACK(&callback, Invoke)); return 0;}

C++需要不斷地練習。

推薦閱讀:

用CPUID檢測各大OJ測評機所用的CPU(以及日常黑BZOJ)

TAG:编程 | C | GTK |