Метод класс как С колбек
От: AHOHUM  
Дата: 09.04.15 07:55
Оценка: :)
Имеется С библиотека с неизменяемым интерфейсом, одна из функций выглядит так:
typedef void(*Callback)(void * internalState, Data * data);
void addCallback(void * internalState, Callback * callback);

internalState – сугубо внутренняя структура библиотеки, data – данные получаемые из библиотеки, т.е. никаких void * userData нет.

Хотелось бы в плюсах 11, чтобы сallback вызывал метод класса. Решение со статическим методом и глобальным this – совсем уж убого.
Не могу понять, возможно ли здесь воспользоваться лямбдой или bind(оба параметра: internalState, data — должны быть доступны):

addCallback(state, [] (void * internalState, Data * data) {});
естественно не имею доступа к полям класса в теле,

addCallback(state, [this]/[&] (void * internalState, Data * data) {});

или

void Class::method(void * internalState, Data * data);
addCallback(state, std::bind(&Class::method, this, std::placeholders::_1, std::placeholders::_2));
тип не сходится.

В чём загвоздка?
Re: Метод класс как С колбек
От: fdn721  
Дата: 09.04.15 08:02
Оценка: +1
Здравствуйте, AHOHUM, Вы писали:

AHO>В чём загвоздка?


В том что лямбда и бинд создают функциональные объекты. И использовать их может только код, который умеет работать с функциональными объектами. Так что ни как.

Что касается С колбек, то скорее всего в internalState есть место где хранить указатель на класс, вы его просто не нашли.
Re: Метод класс как С колбек
От: VTT http://vtt.to
Дата: 09.04.15 08:14
Оценка: +1
Здравствуйте, AHOHUM, Вы писали:

AHO>Имеется С библиотека с неизменяемым интерфейсом


AHO>В чём загвоздка?


std::bind возвращает совсем не Callback *, так что, если интерфейс библиотеки совсем не предполагает хранения указателей на некоторые пользовательские данные, то это придется делать самому. Например, можно держать в статическом поле класса ::std::map< /* internalState */ void *, Class * > и при вызове коллбэка находить соответствующий экземпляр класса.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re: Метод класс как С колбек
От: uzhas Ниоткуда  
Дата: 09.04.15 09:51
Оценка: +1
Здравствуйте, AHOHUM, Вы писали:

AHO>Имеется С библиотека с неизменяемым интерфейсом, одна из функций выглядит так:

AHO>internalState – сугубо внутренняя структура библиотеки, data – данные получаемые из библиотеки, т.е. никаких void * userData нет.

обычно в C библиотеках дают возможность просунуть void* userData, поищите внимательнее, либо сообщите название библиотеки, чтобы мы убедились

AHO>Не могу понять, возможно ли здесь воспользоваться лямбдой или bind(оба параметра: internalState, data — должны быть доступны):


по задумке нельзя. лямбды можно скастить к указателю на функцию только если она не захватывает никаких переменных в контекст

AHO>В чём загвоздка?


на самом деле ваша проблема довольно распространена и существует несколько подходов для ее решения
1) заводим глобальную переменную, кладем туда this, пишем глобальную функцию, которая имеет сигнатуру Callback и которая при вызове сделает перевызов thisObject->CallbackMethod()
2) глобальную переменную еще можно держать в TLS
3) зоопарк переменных можно зашаблонить
typedef void(*ExtendedCallback)(void * internalState, Data * data, void* userData);

template <typename tag>
struct CallbackHolder
{
  static void Func(void * internalState, Data * data)
  {
    UserCallback(internalState, data, UserData);
  }

  static void* UserData = nullptr;
  static ExtendedCallback UserCallback = nullptr;
}

//usage
struct MyTag1 {} ;// create any number of tags
//probably, we need to define static data
void* CallbackHolder<MyTag1>::UserData = nullptr;
ExtendedCallback CallbackHolder<MyTag1>::UserCallback = nullptr;

Callback<MyTag1> c; // track lifetime as needed
Callback fn = &Callback<MyTag1>::Func;
addCallback(state, fn);


4) заморачиваемся с динамическими колбеками-трамплинами. их можно поддерживать динамически или сразу выделить пул из 100 колбеков. рекомендую гуглить по словам trampoline, thunk
на этом форуме тоже замечательно гуглится: http://rsdn.ru/forum/cpp.applied/5116247.flat
Автор: uzhas
Дата: 28.03.13

суть thunk в том, что он содержит секцию для хранения void* (доп данных) + расширенный колбек и содержит ассемблерный код, который перевызывает расширенный кол-бек со всеми данными
метакод
typedef void(*ExtendedCallback)(void * internalState, Data * data, void* userData);

struct ThunkData
{
  <executable code>
  void* UserData;
  ExtendedCallback UserCallback;
};

ThunkData* data = ...; <- get from pool or allocate somehow
data->UserData = myData;
data->UserCallback = mySuperCallback;
Callback fn = (Callback)(data); <--- pointer to executable code inside thunk
addCallback(state, fn);


есть предложения в стандарт языка добавить поддержку таких thunk (ссылку не могу найти)
успехов
Отредактировано 09.04.2015 10:04 uzhas . Предыдущая версия . Еще …
Отредактировано 09.04.2015 9:58 uzhas . Предыдущая версия .
Отредактировано 09.04.2015 9:55 uzhas . Предыдущая версия .
Re[2]: Метод класс как С колбек
От: AHOHUM  
Дата: 09.04.15 10:29
Оценка:
Здравствуйте, fdn721, Вы писали:

F>Здравствуйте, AHOHUM, Вы писали:


AHO>>В чём загвоздка?


F>В том что лямбда и бинд создают функциональные объекты. И использовать их может только код, который умеет работать с функциональными объектами. Так что ни как.


F>Что касается С колбек, то скорее всего в internalState есть место где хранить указатель на класс, вы его просто не нашли.


Спасибо, понял. Внимательно поискал: ни в internalState, ни в data – нет места для сохранения указателя на объект класса. internalState имеет комментарий "internally used data" (и оно действительно так и есть, если более детально взглянуть на данную структуру), data содержит строго типизированные данные, которые ожидаются от библиотеки.
Re[2]: Метод класс как С колбек
От: AHOHUM  
Дата: 09.04.15 10:31
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>Здравствуйте, AHOHUM, Вы писали:


AHO>>Имеется С библиотека с неизменяемым интерфейсом


AHO>>В чём загвоздка?


VTT>std::bind возвращает совсем не Callback *, так что, если интерфейс библиотеки совсем не предполагает хранения указателей на некоторые пользовательские данные, то это придется делать самому. Например, можно держать в статическом поле класса ::std::map< /* internalState */ void *, Class * > и при вызове коллбэка находить соответствующий экземпляр класса.


Спасибо, так и хотел сделать, но думал, что есть более "красивые" методы решения данной проблемы.
Re[2]: Метод класс как С колбек
От: AHOHUM  
Дата: 09.04.15 10:41
Оценка: +1
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, AHOHUM, Вы писали:


AHO>>Имеется С библиотека с неизменяемым интерфейсом, одна из функций выглядит так:

AHO>>internalState – сугубо внутренняя структура библиотеки, data – данные получаемые из библиотеки, т.е. никаких void * userData нет.

U>обычно в C библиотеках дают возможность просунуть void* userData, поищите внимательнее, либо сообщите название библиотеки, чтобы мы убедились


AHO>>Не могу понять, возможно ли здесь воспользоваться лямбдой или bind(оба параметра: internalState, data — должны быть доступны):


U>по задумке нельзя. лямбды можно скастить к указателю на функцию только если она не захватывает никаких переменных в контекст


AHO>>В чём загвоздка?


U>на самом деле ваша проблема довольно распространена и существует несколько подходов для ее решения

U>1) заводим глобальную переменную, кладем туда this, пишем глобальную функцию, которая имеет сигнатуру Callback и которая при вызове сделает перевызов thisObject->CallbackMethod()
U>2) глобальную переменную еще можно держать в TLS
U>3) зоопарк переменных можно зашаблонить
U>
U>typedef void(*ExtendedCallback)(void * internalState, Data * data, void* userData);

U>template <typename tag>
U>struct CallbackHolder
U>{
U>  static void Func(void * internalState, Data * data)
U>  {
U>    UserCallback(internalState, data, UserData);
U>  }

U>  static void* UserData = nullptr;
U>  static ExtendedCallback UserCallback = nullptr;
U>}

U>//usage
U>struct MyTag1 {} ;// create any number of tags
U>//probably, we need to define static data
U>void* CallbackHolder<MyTag1>::UserData = nullptr;
U>ExtendedCallback CallbackHolder<MyTag1>::UserCallback = nullptr;

U>Callback<MyTag1> c; // track lifetime as needed
U>Callback fn = &Callback<MyTag1>::Func;
U>addCallback(state, fn);
U>


U>4) заморачиваемся с динамическими колбеками-трамплинами. их можно поддерживать динамически или сразу выделить пул из 100 колбеков. рекомендую гуглить по словам trampoline, thunk

U>на этом форуме тоже замечательно гуглится: http://rsdn.ru/forum/cpp.applied/5116247.flat
Автор: uzhas
Дата: 28.03.13

U>суть thunk в том, что он содержит секцию для хранения void* (доп данных) + расширенный колбек и содержит ассемблерный код, который перевызывает расширенный кол-бек со всеми данными
U>метакод
U>
U>typedef void(*ExtendedCallback)(void * internalState, Data * data, void* userData);

U>struct ThunkData
U>{
U>  <executable code>
U>  void* UserData;
U>  ExtendedCallback UserCallback;
U>};

U>ThunkData* data = ...; <- get from pool or allocate somehow
data->>UserData = myData;
data->>UserCallback = mySuperCallback;
U>Callback fn = (Callback)(data); <--- pointer to executable code inside thunk
U>addCallback(state, fn);
U>


U>есть предложения в стандарт языка добавить поддержку таких thunk (ссылку не могу найти)

U>успехов

К сожалению, библиотека коммерческая, и мне категорически намекнули никуда её не выкладывать.
Внимательно поискал: ни в internalState, ни в data – нет места для сохранения указателя на объект класса. internalState имеет комментарий "internally used data" (и оно действительно так и есть, если более детально взглянуть на данную структуру), data содержит строго типизированные данные, которые ожидаются от библиотеки.

1) Вот от этого и хотел уйти, т.к. думал, что есть более "красивые" методы решения данной проблемы.
2) Тоже рассматривал как вариант.
3) Видимо это и буду использовать.
3) Да, конечно, я предварительно загуглил данную тему здесь, нашёл замечательную статью 2004 года. Просто вполне отдавал себе отчёт, что к 2015 году уже что-то есть в стандарте, что я мог запросто пропустить. В моём случае это будет из пушки по воробьям.

Спасибо за развернутый ответ, думаю, я определился с вариантом.
Re: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 09.04.15 11:28
Оценка: 1 (1)
Здравствуйте, AHOHUM, Вы писали:

AHO>Не могу понять, возможно ли здесь воспользоваться лямбдой или bind(оба параметра: internalState, data — должны быть доступны):


Вот здесь
Автор: Nikita.Trophimov
Дата: 02.04.13
есть пример как можно автоматически сохранить функциональный объект (лямбду/результат bind) в глобальный нумерованный объект и получить указатель на функцию. Например:
addCallback(internal_state, make_wrapper<42>(bind(&Foo::method, foo, _1, _2)) );

Там же там есть ссылка на упомянутый uzhas'ом proposal по thunk'ам.
Re: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 09.04.15 11:42
Оценка:
Здравствуйте, AHOHUM, Вы писали:

AHO>Имеется С библиотека с неизменяемым интерфейсом, одна из функций выглядит так:

AHO>
AHO>typedef void(*Callback)(void * internalState, Data * data);
AHO>void addCallback(void * internalState, Callback * callback);
AHO>

AHO>internalState – сугубо внутренняя структура библиотеки, data – данные получаемые из библиотеки, т.е. никаких void * userData нет.

1. Где создаётся и удаляется internalState? На нашей стороне?
2. В callback передаётся именно тот указатель internalState который был передан в addCallback?

I. Если на оба вопроса ответ да — то можно к нему прицепом нагрузить наши данные. То есть:
struct StateWithPayload
{
    OurData payload;
    InternalState internalState;
};
А callback будет доставать payload через offsetof.

II. Если ответ да только на второй вопрос — то можно создать отображение из *internalState в наши данные, например unordered_map<void*, OurData> — и callback будет знать из какой ячейки доставать payload.
Отредактировано 09.04.2015 11:44 Evgeny.Panasyuk . Предыдущая версия .
Re[2]: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 09.04.15 12:08
Оценка:
AHO>Имеется С библиотека с неизменяемым интерфейсом, одна из функций выглядит так:
AHO>
AHO>typedef void(*Callback)(void * internalState, Data * data);
AHO>void addCallback(void * internalState, Callback * callback);
AHO>

AHO>internalState – сугубо внутренняя структура библиотеки, data – данные получаемые из библиотеки, т.е. никаких void * userData нет.

Ещё, как вариант (если другие не подходят), можно прятать указатель/индекс данных в padding'е internalState — если конечно его достаточно, и он нигде не теряется/не затирается.
Re[2]: Метод класс как С колбек
От: AHOHUM  
Дата: 09.04.15 12:34
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, AHOHUM, Вы писали:


AHO>>Имеется С библиотека с неизменяемым интерфейсом, одна из функций выглядит так:

AHO>>
AHO>>typedef void(*Callback)(void * internalState, Data * data);
AHO>>void addCallback(void * internalState, Callback * callback);
AHO>>

AHO>>internalState – сугубо внутренняя структура библиотеки, data – данные получаемые из библиотеки, т.е. никаких void * userData нет.

EP>1. Где создаётся и удаляется internalState? На нашей стороне?

EP>2. В callback передаётся именно тот указатель internalState который был передан в addCallback?

EP>I. Если на оба вопроса ответ да — то можно к нему прицепом нагрузить наши данные. То есть:

EP>
EP>struct StateWithPayload
EP>{
EP>    OurData payload;
EP>    InternalState internalState;
EP>};
EP>
А callback будет доставать payload через offsetof.


EP>II. Если ответ да только на второй вопрос — то можно создать отображение из *internalState в наши данные, например unordered_map<void*, OurData> — и callback будет знать из какой ячейки доставать payload.


1) Объявлен как поле класса, создается посредствам create(&internalState, …) этой же библиотеки.
2) Да, именно он.
Сейчас сделано как раз на unordered_map с ключом internalState, разбираюсь с 3м методом предложенным товарищем uzhas'ом.
Re: Метод класс как С колбек
От: beyv  
Дата: 09.04.15 14:30
Оценка:
Здравствуйте, AHOHUM, Вы писали:

Рекомендую FastDelegate, сам много лет использую для колбеков всех мастей.
Желательно прочитать статью чтобы понять как оно работает.
Re[2]: Метод класс как С колбек
От: sokel Россия  
Дата: 09.04.15 16:08
Оценка:
Здравствуйте, uzhas, Вы писали:

U>3) зоопарк переменных можно зашаблонить


Если с тегами, то можно так примерно через std::function:

#include <iostream>
#include <functional>
#include <cassert>

template<typename Function, typename Tag = void>
struct c_callback_holder { static std::function<Function> fn; };
template<typename Function, typename Tag>
std::function<Function> c_callback_holder<Function, Tag>::fn;

template<typename Function, typename Tag = void>
struct c_callback;
template<typename Tag, typename Ret, typename ...Args>
struct c_callback<Ret(Args...), Tag> {
    static Ret call(Args... args) {
        return c_callback_holder<Ret(Args...), Tag>::fn(std::forward<Args>(args)...);
    }
};
template<typename Function, typename Tag, typename F>
Function* set_callback(F&& f) {
    c_callback_holder<Function, Tag>::fn = std::forward<F>(f);
    return &c_callback<Function, Tag>::call;
}

struct foo {
    int i;
    foo(int i) : i(i) {}
    void test() { std::cout << "Hello, world!!! " << i << std::endl; }
};

int main() {

    using callback_fn = void();
    foo obj1(33);
    callback_fn* f1 = set_callback<callback_fn, foo>(std::bind(&foo::test, &obj1));
    f1();
    foo obj2(42);
    callback_fn* f2 = set_callback<callback_fn, foo>(std::bind(&foo::test, &obj2));
    assert(f1 == f2);
    f2();
    return 0;
}
Re[3]: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 09.04.15 16:22
Оценка:
Здравствуйте, sokel, Вы писали:

U>>3) зоопарк переменных можно зашаблонить

S>Если с тегами, то можно так примерно через std::function:

std::function там не нужен. В качестве тэгов проще использовать числа чем типы. http://rsdn.ru/forum/cpp/5121566.1
Автор: Evgeny.Panasyuk
Дата: 02.04.13
Re[3]: Метод класс как С колбек
От: sokel Россия  
Дата: 09.04.15 16:27
Оценка:
Здравствуйте, sokel, Вы писали:

S>Здравствуйте, uzhas, Вы писали:


U>>3) зоопарк переменных можно зашаблонить


S>Если с тегами, то можно так примерно через std::function:

О, а в качестве тега можно тип внешней функций запользовать:

#include <iostream>
#include <functional>

template<typename Function, typename Tag = void>
struct c_callback_holder { static std::function<Function> fn; };
template<typename Function, typename Tag>
std::function<Function> c_callback_holder<Function, Tag>::fn;

template<typename Function, typename Tag = void>
struct c_callback;
template<typename Tag, typename Ret, typename ...Args>
struct c_callback<Ret(Args...), Tag> {
    static Ret call(Args... args) {
        return c_callback_holder<Ret(Args...), Tag>::fn(std::forward<Args>(args)...);
    }
};
template<typename Function, typename F>
Function* set_callback(F&& f) {
    c_callback_holder<Function, F>::fn = std::forward<F>(f);
    return &c_callback<Function, F>::call;
}

struct foo {
    int i;
    foo(int i) : i(i) {}
    void test() { std::cout << "Hello, world!!! " << i << std::endl; }
};

int main() {

    using callback_fn = void();
    foo obj1(33);
    foo obj2(42);
    callback_fn* f1 = set_callback<void()>([&obj1]() { obj1.test(); });
    callback_fn* f2 = set_callback<void()>([&obj2]() { obj2.test(); });
    f1();
    f2();
    return 0;
}
Re[4]: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 09.04.15 16:32
Оценка:
Здравствуйте, sokel, Вы писали:

S>О, а в качестве тега можно тип внешней функций запользовать:


Здесь
Автор: Evgeny.Panasyuk
Дата: 02.04.13
он и так используется, только вдобавок к нему есть ещё и численный тэг.
Re[4]: Метод класс как С колбек
От: sokel Россия  
Дата: 09.04.15 16:36
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, sokel, Вы писали:


U>>>3) зоопарк переменных можно зашаблонить

S>>Если с тегами, то можно так примерно через std::function:

EP>std::function там не нужен.

std::funсtion зато не только thiscall, более универсальный.

EP>В качестве тэгов проще использовать числа чем типы. http://rsdn.ru/forum/cpp/5121566.1
Автор: Evgeny.Panasyuk
Дата: 02.04.13

Ну если это вот для маленького кусочка только, то да, наверное. А если в паре мест понадобится?
Re[5]: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 09.04.15 16:43
Оценка:
Здравствуйте, sokel, Вы писали:

EP>>std::function там не нужен.

S>std::funсtion зато не только thiscall, более универсальный.

Так я и не говорю что только thiscall — там может быть любой функциональный объект, что результат bind'а, что лямбда и т.п.
std::function нужен когда требуется стереть тип, а здесь же наоборот — чем конкретней тип, тем проще избежать коллизии.

EP>>В качестве тэгов проще использовать числа чем типы. http://rsdn.ru/forum/cpp/5121566.1
Автор: Evgeny.Panasyuk
Дата: 02.04.13

S>Ну если это вот для маленького кусочка только, то да, наверное. А если в паре мест понадобится?

А, ты вон про что — да, тогда лучше объявлять локальный тип-тэг для каждого translation unit.
Re: Метод класс как С колбек
От: Zenden Россия  
Дата: 09.04.15 17:06
Оценка:
Здравствуйте, AHOHUM, Вы писали:

AHO>Имеется С библиотека с неизменяемым интерфейсом, одна из функций выглядит так:

AHO>
AHO>typedef void(*Callback)(void * internalState, Data * data);
AHO>void addCallback(void * internalState, Callback * callback);
AHO>

AHO>internalState – сугубо внутренняя структура библиотеки, data – данные получаемые из библиотеки, т.е. никаких void * userData нет.

Для этого придумали Thunking.

http://zabkat.com/blog/hook-callback-thunk-x64.htm
http://www.codeproject.com/Articles/16785/Thunking-in-Win-Simplifying-Callbacks-to-Non-sta
http://www.codeproject.com/Articles/6731/Using-class-methods-as-callbacks
http://dj9okeyxktdvd.cloudfront.net/Articles/5717/Better-way-to-use-member-function-for-C-style-call

Одна проблема — это либо не компилируется / вылетает / либо статья старая, и автор не учитывает такую крутую штуку, как DEP.
А еще оно может съесть первый аргумент функции.

Не подскажет аль кто живой проект, и чтоб оно не съедало первый аргумент функции?
Отредактировано 09.04.2015 17:11 Zenden . Предыдущая версия . Еще …
Отредактировано 09.04.2015 17:07 Zenden . Предыдущая версия .
Re[6]: Метод класс как С колбек
От: sokel Россия  
Дата: 09.04.15 21:50
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>std::function нужен когда требуется стереть тип, а здесь же наоборот — чем конкретней тип, тем проще избежать коллизии.


Да, согласен. Просто быстрый вариант отложенного конструктора вместо optional
Инты можно легко в теги завернуть если что, ну а сигнатуру то всё равно нужно отдельным параметром передавать.

Кстати, optional не съест noncopyable. Можно свой holder сделать, без требования copy-constructible:

#include <iostream>
#include <functional>
#include <new>
#include <type_traits>
#include <memory>

template<typename T>
struct functor_holder {
    using value_type = typename std::decay<T>::type;
    using storage_type = typename std::aligned_storage<sizeof(value_type), std::alignment_of<value_type>::value>::type;
    bool constructed = false;
    storage_type data;
    ~functor_holder() { destruct(); }
    T& get() { return *(reinterpret_cast<value_type*>(&data)); }
    void destruct() { if(constructed) reinterpret_cast<value_type*>(&data)->~value_type(); }
    template<typename U>
    void set(U&& v) {
        destruct();
        new (static_cast<void*>(&data)) value_type(std::forward<U>(v));
        constructed = true;
    }
};

template<typename Function, typename Functor, typename Tag>
struct c_callback;
template<typename Functor, typename Tag, typename Ret, typename ...Args>
struct c_callback<Ret(Args...), Functor, Tag> {
    static functor_holder<Functor> fn;
    static Ret call(Args... args) {
        return fn.get()(std::forward<Args>(args)...);
    }
};
template<typename Functor, typename Tag, typename Ret, typename ...Args>
functor_holder<Functor> c_callback<Ret(Args...), Functor, Tag>::fn;

template<typename Function, typename Tag, typename Functor>
Function* set_callback(Functor&& f) {
    c_callback<Function, Functor, Tag>::fn.set(std::forward<Functor>(f));
    return &c_callback<Function, Functor, Tag>::call;
}
template<int N> struct int_tag;
template<typename Function, int N, typename Functor>
Function* set_callback(Functor&& f) {
    return set_callback<Function, int_tag<N>, Functor>(std::forward<Functor>(f));
}

struct foo {
    int i;
    foo(int i) : i(i) {}
    void fn1() { std::cout << i << ": Hello, world!!!" << std::endl; }
    void fn2() { std::cout << i << ": Bye, world!!!" << std::endl; }
};

int main() {
    foo obj(123);
    auto f1 = set_callback<void(), 1>(std::bind(&foo::fn1, &obj));
    auto f2 = set_callback<void(), 2>(std::bind(&foo::fn2, &obj));
    f1();
    f2();
    auto o = std::make_unique<foo>(42);
    auto f = set_callback<void(), 2>(std::bind(&foo::fn2, std::move(o)));
    f();    
    return 0;
}
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.