Это способ сделать отличный ответ @ Холта с немного лучшим синтаксисом.Это не завершено, потому что есть шаблон, который нужно сделать.
template<class C, class M = decltype(&C::run)>
struct run_as_lambda_impl;
// non-const non-volatile non-ref qualified noexcept(false) case:
template<class C, class R, class...Args>
struct run_as_lambda_impl<C, R(C::*)(Args...)>: C {
std::function<R(Args...)> f;
R run(Args...) final override {
return static_cast<R>(f( std::forward<Args>(args)... ));
}
};
Вам понадобится 3 квалификационных отборочных, раз 2 квалификационных отборочных, временные 2 отборочных квалификационных, времена не исключая истину / ложь для 24 различныхверсии этого.
теперь представьте макрос:
#define RUN_AS_LAMBDA_TYPE( CLASS ) \
run_as_lambda_impl< CLASS >
это жесткие коды run
как метод, который мы переопределяем, но не жестко кодируем сигнатуру.Кроме того, мы печатаем лямбда, но пока я в порядке.
Мы можем обойти это.
#define BASE_LAMBDA_TEMPLATE( NAME, METHOD ) \
template<class C, class M = decltype(&C::METHOD)> \
struct NAME
#define LAMBDA_TEMPLATE_IMPL( NAME, METHOD, QUALS ) \
template<class C, class R, class...Args> \
struct NAME<C, R(C::*)(Args...) QUALS> : C { \
std::function<R(Args...)> f; \
NAME( std::function<R(Args...)> fin ): f(std::move(fin)) {} \
R METHOD(Args...) QUALS final override { \
return static_cast<R>( f( std::forward<Args>(args)... ) ); \
} \
}
#define LAMBDA_TEMPLATE( NAME, METHOD ) \
BASE_LAMBDA_TEMPLATE( NAME, METHOD ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, & ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, && ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, const ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, const & ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, const && ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile & ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile && ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile const ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile const & ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile const && ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, & noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, && noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, const noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, const & noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, const && noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile & noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile && noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile const noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile const & noexcept(true) ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, volatile const && noexcept(true) )
, что означает 24 различных LAMBDA_TEMPLATE_IMPL
вызова для 3 *2 * 2 * 2 * 2 типа переопределений методов.Вы можете уменьшить это:
#define LAMBDA_TEMPLATE_IMPL_C( NAME, METHOD, QUALS ) \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, QUALS ); \
LAMBDA_TEMPLATE_IMPL( NAME, METHOD, const QUALS )
#define LAMBDA_TEMPLATE_IMPL_CV( NAME, METHOD, QUALS ) \
LAMBDA_TEMPLATE_IMPL_C( NAME, METHOD, QUALS ); \
LAMBDA_TEMPLATE_IMPL_C( NAME, METHOD, volatile QUALS )
#define LAMBDA_TEMPLATE_IMPL_CVR( NAME, METHOD, QUALS ) \
LAMBDA_TEMPLATE_IMPL_CV( NAME, METHOD, QUALS ); \
LAMBDA_TEMPLATE_IMPL_CV( NAME, METHOD, & QUALS ); \
LAMBDA_TEMPLATE_IMPL_CV( NAME, METHOD, && QUALS )
#define LAMBDA_TEMPLATE_IMPL_CVRE( NAME, METHOD ) \
LAMBDA_TEMPLATE_IMPL_CVR( NAME, METHOD, ); \
LAMBDA_TEMPLATE_IMPL_CVR( NAME, METHOD, noexcept(true) )
Тогда:
#define LAMBDA_TEMPLATE( NAME, METHOD ) \
BASE_LAMBDA_TEMPLATE( NAME, METHOD ); \
LAMBDA_TEMPLATE_IMPL_CVRE( NAME, METHOD )
, что составляет 3 + 3 + 3 + 3 + 4 = 16 строк вместо 24.
Предположим, у вас есть эти два интерфейса:
class SimpleTask {
public:
virtual void run() = 0;
};
class ComplexTask {
public:
virtual int do_stuff(int, double) = 0;
};
, тогда вы можете написать
LAMBDA_TEMPLATE( LambdaRun, run );
LAMBDA_TEMPLATE( LambdaDoTask, do_task );
, и мы можем использовать LambdaRun<SimpleTask>{ []{std::cout << "I ran\n"; } }
в качестве реализации на основе лямбды SimpleTask
.
Аналогично, LambdaDoTask<ComplexTask>{ [](auto a, auto b) { return a+b; } }
.
Это не очень похоже на Java.Java гораздо более OO-ориентированный язык, чем C ++;в C ++ OO-дизайн - это опция .
C ++ lambdas создает вызываемые объекты, которые переопределяют operator()
.Если у вас есть что-то, что «может быть выполнено с подписью», идиоматический способ сделать это в C ++ - это использовать std::function<void()>
или аналогичный.
std::function
использует семантику значений;внутренне вы можете хранить указатель внутри значения, если хотите.
Так что в C ++ вы захотите:
using SimpleTask = std::function<void()>;
, а остальная часть вашего кода теперь тривиальна:
Thread myThread(L"MyTestingThread", []{ /* code */} );
myThread.start();
, поскольку лямбда может быть напрямую преобразована в std::function<void()>
, если подписи совместимы.
Часть этого переходит на семантику значений.
Но за исключением этоговам захочется