Все мы знаем, что лямбду можно передать, как указатель на функцию, только без захвата контекста: Код (C++): int func(int (*callback)(int arg), int arg) { return callback(arg); } int main() { int result = func([](int arg) -> int { return arg * 2; }, 10); } Однако, используя std::bind, можно сделать нечто подобное: Код (C++): typedef std::function<int(int, float)> func_t; void func(void* arg) { auto pbind = (std::_Binder<int, func_t, int, float>*)arg; (*pbind)(); } int main() { func_t f = [&](int a, float b) -> int { return a * 2; }; auto bind = std::bind(f, 1, 2.0f); func(&bind); } Осталось это шаблонизировать, чтобы работать с произвольным числом аргументов произвольных типов. Как составить шаблон, чтобы построить std::_Binder на основе std::function без ограничений на количество и типы аргументов? А именно - вывести тип переданного _Binder'a. Кроме того, std::bind не проверяет типы аргументов (например, вместо int можно передать строку) - как гарантировать, что все переданные аргументы нужных типов? Конечная хотелка - сделать возможность передавать лямбды с захватом контекста в функции, принимающие только сырые указатели на каллбэки. Например, хочу написать стаб, позволяющий передавать в CreateThread лямбды с контекстом: некую прослойку, которая вытащит из переданного указателя std::bind и вызовет лямбду - по аналогии, как сделано в std::thread. --- Сообщение объединено, 30 июн 2019 --- По аналогии с std::thread сделал такое, фактически копипастом: Код (C++): class ThreadWrapper { public: template <class Tuple, size_t... indices> static void __stdcall stub(void* arg) { const std::unique_ptr<Tuple> fn_vals(static_cast<Tuple*>(arg)); Tuple& tuple = *fn_vals; std::invoke(std::move(std::get<indices>(tuple))...); } template <class Tuple, size_t... indices> static constexpr auto get_invoke(std::index_sequence<indices...>) { return &stub<Tuple, indices...>; } template <class Func, class... Args> static void call(Func&& func, Args&&... args) { using FuncTuple = std::tuple<std::decay_t<Func>, std::decay_t<Args>...>; auto decay_copied = std::make_unique<FuncTuple>(std::forward<Func>(func), std::forward<Args>(args)...); constexpr auto invoker = get_invoke<FuncTuple>(std::make_index_sequence<1 + sizeof...(Args)>{}); invoker(decay_copied.get()); decay_copied.release(); } }; int main() { int c = 10; ThreadWrapper::call([&](int a, float b) -> int { int d = a * 2; printf("c = %i\r\n", c); return d; }, 123, 0.02f); } И это даже работает. Но совершенно не понимаю, как. Знатоки шаблонной магии, объясните, что здесь происходит и почему?
Это вообще не проблема, т.к. CreateThread принимает кроме начального адреса ещё указатель на аргумент для вызываемой функции. Проблема — это когда принимается только указатель на функцию.
Да, уже сделал по примеру выше. Но всё равно, шаблонная магия выглядит страшно... Этого, к счастью, не требуется
проблема в том, что ты пытаешься прикрутить плюсы к сишечным апи, которые ничего и знать не знают о захвате контекста и лямбдах... зачем тебе использовать CreateThread в плюсах, когда есть готовый std::thread? при этом std::thread чисто теоретически может инициализировать для потока какие-либо глобальный переменные/данные/конструкторы, которые необходимы для корректной работы плюсового рантайма, как некоторые писали о _beginthread и _beginthreadex...
Для единообразия и чтобы понять, как вообще они сумели сделать передачу контекста по сырым указателям: пишу обёртки над потоками, чтобы собрать в одном классе все специфичные для Win32-потоков вещи - контексты, заморозку, APC, и сделать это, в первую очередь, удобным. А удобство, в том числе, и в поддержке полноценных лямбд. Пока полёт нормальный. А что про них писали? Что там может вылезти, если юзать CreateThread в плюсовом окружении? Всегда юзал CreateThread и ни разу не встречал каких-то проблем.
чес сказать - то тут самое адекватное - не толкать указатели в стек или регистры, а просто использовать положительные оффсеты rbp/rsp/ebp/esp