Метод класс как С колбек
От: 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;
}
Re[7]: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 09.04.15 22:20
Оценка:
Здравствуйте, sokel, Вы писали:

S>ну а сигнатуру то всё равно нужно отдельным параметром передавать.


Можно выводить — если конечно то что выведется устроит. Но вывод не всегда возможен, например с шаблонами или перегрузкой — так что тут зависит от ситуации, думаю явная сигнатура предпочтительней.

S>Кстати, optional не съест noncopyable.


Почему? Судя по документации — должен: 1, 2.

Это как раз std::function noncopyable не съест — особенности type-erasure (тут как раз нужен специальный тип a-la move_function).
Re[2]: Метод класс как С колбек
От: AHOHUM  
Дата: 10.04.15 05:57
Оценка:
Здравствуйте, beyv, Вы писали:

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


B>Рекомендую FastDelegate, сам много лет использую для колбеков всех мастей.

B>Желательно прочитать статью чтобы понять как оно работает.

Да, именно про эту статью я и писал выше, тут даже есть перевод её здесь
Автор(ы): Don Clugston
Дата: 27.07.2005
В данной статье предоставлен исчерпывающий материал по указателям на функции-члены, а также приведена реализация делегатов, которые занимают всего две операции на ассемблере.
датированный 2004 годом, замечательная статья, но как уже писал, предполагал, что за прошедшее время могло что-нибудь появиться и в стандарте.
Re[3]: Метод класс как С колбек
От: Zenden Россия  
Дата: 10.04.15 06:17
Оценка:
Здравствуйте, AHOHUM, Вы писали:

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


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


B>>Рекомендую FastDelegate, сам много лет использую для колбеков всех мастей.

B>>Желательно прочитать статью чтобы понять как оно работает.

AHO>Да, именно про эту статью я и писал выше, тут даже есть перевод её здесь
Автор(ы): Don Clugston
Дата: 27.07.2005
В данной статье предоставлен исчерпывающий материал по указателям на функции-члены, а также приведена реализация делегатов, которые занимают всего две операции на ассемблере.
датированный 2004 годом, замечательная статья, но как уже писал, предполагал, что за прошедшее время могло что-нибудь появиться и в стандарте.


Разве оно умеет "Метод класс как С колбек" ?
Re[8]: Метод класс как С колбек
От: sokel Россия  
Дата: 10.04.15 09:37
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Это как раз std::function noncopyable не съест — особенности type-erasure (тут как раз нужен специальный тип a-la move_function).


std::function это да, там копирование не выпиливается потому что виртуализируется.

EP>Почему? Судя по документации — должен: 1, 2.


А с boost — ну у меня, видимо, boost старый — 1.55. Но похоже ещё и у bind с move присваиванием проблемы могут быть...
Свой storage для функтора позволяет понизить требование с move-assignable до move-constructible.
Или я что то не так делаю?

#include <iostream>
#include <memory>
#include <boost/optional.hpp>

// примерный optional, присваивание через perfect forward
template<typename T>
struct opt_move_assign {
    ~opt_move_assign() {
        if(constructed)
            reinterpret_cast<T*>(&data)->~T();
    }
    template<typename U>
    opt_move_assign& operator=(U&& x) {
        if(constructed)
            *(reinterpret_cast<T*>(&data)) = std::forward<U>(x);
        else {
            new (&data) T(std::forward<T>(x));
            constructed = true;
        }
        return *this;
    }
    bool constructed = false;
    typename std::aligned_storage<sizeof(T)>::type data;
};
// примерный optional, присваивание не используется
template<typename T>
struct opt_move_construct {
    ~opt_move_construct() {
        if(constructed)
            reinterpret_cast<T*>(&data)->~T();
    }
    template<typename U>
    opt_move_construct& operator=(U&& x) {
        if(constructed) {
            constructed = false;
            reinterpret_cast<T*>(&data)->~T();
        }
        new (&data) T(std::forward<T>(x));
        constructed = true;
        return *this;
    }
    bool constructed = false;
    typename std::aligned_storage<sizeof(T)>::type data;
};

template<template<class> class Storage, class F>
void test(F&& f) {
    using store_type = std::decay<F>::type;
    Storage<store_type> x;
    x = std::forward<F>(f);
}


struct foo {
    void bar() {}
};

int main() {
    // вот это ок, компилится
    test<opt_move_assign>(std::make_unique<int>(111));
    test<opt_move_construct>(std::make_unique<int>(111));

    // вот это уже нет (boost 1.55)
    // test<boost::optional>(std::make_unique<int>(111));

    // а вот тут уже bind, вылезает copy tuple
    // test<opt_move_assign>(std::bind(&foo::bar, std::make_unique<foo>()));
    // хотя через move-construct всё ok
    test<opt_move_construct>(std::bind(&foo::bar, std::make_unique<foo>()));

    // ну как и в предыдушем варианте, тоже беда...
    // test<boost::optional>(std::bind(&foo::bar, std::make_unique<foo>()));
    return 0;
}
Re[9]: Метод класс как С колбек
От: sokel Россия  
Дата: 10.04.15 11:21
Оценка:
Здравствуйте, sokel, Вы писали:

S>Но похоже ещё и у bind с move присваиванием проблемы могут быть...

S>Свой storage для функтора позволяет понизить требование с move-assignable до move-constructible.
S>Или я что то не так делаю?

А, ну собственно по стандарту никто и не требует от bind быть MoveAssignable:

The return type shall satisfy the requirements of MoveConstructible. If all of FD and TiD
satisfy the requirements of CopyConstructible, then the return type shall satisfy the requirements
of CopyConstructible. [ Note: This implies that all of FD and TiD are MoveConstructible. — end
note ]

Re[4]: Метод класс как С колбек
От: beyv  
Дата: 10.04.15 11:39
Оценка:
Здравствуйте, Zenden, Вы писали:

Z>Разве оно умеет "Метод класс как С колбек" ?


Да, умеет (если вставить недостающие буквы в "Метод класс как С колбек" )

там есть примеры в FastDelegateDemo.cpp

примерно так

using namespace fastdelegate;

// Это класс
class CClass {
public:
    // Это метод класса
    int Function(int num, char *str);
};

int main(void){

    // Обьявляете тип делегата (параметр1, параметр2, тип результата)
    typedef FastDelegate2<int, char *, int> FunctionDelegate;

    // Создаете класс
    CClass a();

    // "deleg" - переменная-колбек, цепляем на неё метод класса
    FunctionDelegate deleg = MakeDelegate(&a, &CClass::Function);

    // вызываем метод класса как простую функцию
    int ret = deleg(5, "foo"); 
}
Re[5]: Метод класс как С колбек
От: Zenden Россия  
Дата: 10.04.15 13:31
Оценка:
Здравствуйте, beyv, Вы писали:

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


Z>>Разве оно умеет "Метод класс как С колбек" ?


B>Да, умеет (если вставить недостающие буквы в "Метод класс как С колбек" )


B>там есть примеры в FastDelegateDemo.cpp


Неа, не умеет. Задачу автора (и мою) не решает.
У fastdelegate просто перегружен operator(), а нужен простой указатель на фукнцию.
Re[6]: Метод класс как С колбек
От: beyv  
Дата: 10.04.15 14:07
Оценка:
Здравствуйте, Zenden, Вы писали:

Z>Неа, не умеет. Задачу автора (и мою) не решает.

Z>У fastdelegate просто перегружен operator(), а нужен простой указатель на фукнцию.

Даа, умеет. Задача автора: "Метод класс как С колбек" а не "простой указатель на фукнцию"
Переменную "FunctionDelegate deleg" (из примера) можно использовать как угодно — брать адрес, передавать как параметр, запускать в космос...
Если "задача" у вас поизвращатся и пытатся изобрести велосипед, ну чтож...
Re[7]: Метод класс как С колбек
От: Zenden Россия  
Дата: 10.04.15 14:40
Оценка:
Здравствуйте, beyv, Вы писали:

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


Z>>Неа, не умеет. Задачу автора (и мою) не решает.

Z>>У fastdelegate просто перегружен operator(), а нужен простой указатель на фукнцию.

B>Даа, умеет. Задача автора: "Метод класс как С колбек" а не "простой указатель на фукнцию"

B>Переменную "FunctionDelegate deleg" (из примера) можно использовать как угодно — брать адрес, передавать как параметр, запускать в космос...
B>Если "задача" у вас поизвращатся и пытатся изобрести велосипед, ну чтож...

На этом, как вы говорите, "извращении" немало библиотек построено — например ATL, WTL.
Задача дана вполне конкретная, общего решения так и не предложено.
fastdelegate хорошая вещь, я ее использую в своих проектах, но в данном случае она приведена не к месту.
Отредактировано 10.04.2015 14:44 Zenden . Предыдущая версия . Еще …
Отредактировано 10.04.2015 14:42 Zenden . Предыдущая версия .
Отредактировано 10.04.2015 14:41 Zenden . Предыдущая версия .
Re[7]: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 10.04.15 14:41
Оценка:
Здравствуйте, beyv, Вы писали:

B>Даа, умеет. Задача автора: "Метод класс как С колбек" а не "простой указатель на фукнцию"


Прочитай изначальное сообщение, а не только его заголовок.
Re[8]: Метод класс как С колбек
От: beyv  
Дата: 10.04.15 15:54
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


B>>Даа, умеет. Задача автора: "Метод класс как С колбек" а не "простой указатель на фукнцию"


EP>Прочитай изначальное сообщение, а не только его заголовок.


Прочитайте исходные код FastDelegate, прежде чем умничать.
Основная загвоздка у ТС получения доступа к полям класса в теле колбека или где там.

Пожалуйста, получите от делегата указатель "This", приведите его к классу и вот вам класс


#include "stdafx.h"
#include "FastDelegate.h"

using namespace fastdelegate;

// Это класс
class CClass {
protected:
    char *m_name;
public:
    CClass(char *name) : m_name(name) {};

    // Это метод класса
    int Function(int num, char *str) {
        printf("In Function in %s. Num=%d, str = %s\n", m_name, num, str); return -1;
    }

};

int _tmain(int argc, _TCHAR* argv[])
{
    typedef FastDelegate2<int, char *, int> FunctionDelegate;
    // Создаете класс
    CClass a("Base A");

    // "deleg" - переменная-колбек
    FunctionDelegate deleg;

    // цепляем на неё метод класса и можем передать его куда угодно
    deleg = MakeDelegate(&a, &CClass::Function);

    // можем использовать как функцию
    deleg(7, "tr");
    
    // Чуть поизвращаясь, можно получить указатель на сам класс
    const detail::ClosurePtr<int, char *, int>& p = (const detail::ClosurePtr<int, char *, int>&)deleg.GetMemento();
    CClass* ap = (CClass*)p.GetClosureThis();

    // Получили класс со всеми потрохами
    ap->Function(77, "ааааа");

    return 0;
}
Отредактировано 10.04.2015 15:59 beyv . Предыдущая версия .
Re[9]: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 10.04.15 16:09
Оценка:
Здравствуйте, beyv, Вы писали:

B>>>Даа, умеет. Задача автора: "Метод класс как С колбек" а не "простой указатель на фукнцию"

EP>>Прочитай изначальное сообщение, а не только его заголовок.
B>Прочитайте исходные код FastDelegate,

И в чём там принципиальное отличие от std::function в рамках вопроса ТС?

B>прежде чем умничать.


О, вот уже и хамство пошло в ход — классный аргумент, главное универсальный

B>Основная загвоздка у ТС получения доступа к полям класса в теле колбека или где там.

B>Пожалуйста, получите от делегата указатель "This", приведите его к классу и вот вам класс

И что это даст в случае ТС?

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

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

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

AHO>Хотелось бы в плюсах 11, чтобы сallback вызывал метод класса. Решение со статическим методом и глобальным this – совсем уж убого.

Эта библиотека принимает только указатель на функцию, и не позволяет прямым способом передать контекст — чем тут поможет этот FastDelegate?
Re[10]: Метод класс как С колбек
От: beyv  
Дата: 10.04.15 16:28
Оценка: :))
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>[/q]

EP>Эта библиотека принимает только указатель на функцию, и не позволяет прямым способом передать контекст — чем тут поможет этот FastDelegate?

В с(++) есть скобочки:

addCallback(void * internalState, (Callback *)хоть-черт-с-ушами);


Для тех (пардон) кто совсем в танке:

FunctionDelegate deleg;
addCallback(void * internalState, (Callback *)&deleg);
Re[10]: Метод класс как С колбек
От: beyv  
Дата: 10.04.15 16:34
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>И в чём там принципиальное отличие от std::function в рамках вопроса ТС?


FastDelegate инкапсулирует в себе и "this" и "метод класса"

EP>О, вот уже и хамство пошло в ход — классный аргумент, главное универсальный


Как и сам язык с++
Re[11]: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 10.04.15 16:40
Оценка:
Здравствуйте, beyv, Вы писали:

EP>>И в чём там принципиальное отличие от std::function в рамках вопроса ТС?

B>FastDelegate инкапсулирует в себе и "this" и "метод класса"

Точно также как и std::function + std::bind, только проблему ТС это никак не решает
Re[11]: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 10.04.15 16:42
Оценка: +2
Здравствуйте, beyv, Вы писали:

EP>>Эта библиотека принимает только указатель на функцию, и не позволяет прямым способом передать контекст — чем тут поможет этот FastDelegate?

B>В с(++) есть скобочки:
B>
B>addCallback(void * internalState, (Callback *)хоть-черт-с-ушами);
B>

B>Для тех (пардон) кто совсем в танке:
B>
B>FunctionDelegate deleg;
B>addCallback(void * internalState, (Callback *)&deleg);
B>


Очевидно что у ТС задача чтобы код работал, без вылетов и UB, а не просто компилировался
Re[12]: Метод класс как С колбек
От: beyv  
Дата: 10.04.15 16:51
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


EP>>>И в чём там принципиальное отличие от std::function в рамках вопроса ТС?

B>>FastDelegate инкапсулирует в себе и "this" и "метод класса"

EP>Точно также как и std::function + std::bind, только проблему ТС это никак не решает


Ну вы даете, я вам нарисовал как сделать делегата(колбек) на экземпляр любого класса и любой его метод(хоть с 6 параметрами),

как передать его в "void addCallback(void * internalState, Callback * callback);" вместо "callback"

как извлечь из переданого делегата экземпляр переданого класса и делать с ним что угодно

и это не решает проблему ТС ?
Боюсь даже спрашивать какая ещё тут проблема, кривые руки? непонимание чужого кода?
Re[12]: Метод класс как С колбек
От: beyv  
Дата: 10.04.15 16:56
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Очевидно что у ТС задача чтобы код работал, без вылетов и UB, а не просто компилировался


Чтобы поломать код которым люди пользуются 9 лет со всеми существующими компиляторами надо иметь особое упорство
Re[2]: Метод класс как С колбек
От: Zenden Россия  
Дата: 10.04.15 19:14
Оценка:
Вот один из работающих вариантов:
http://p-nand-q.com/programming/cplusplus/using_member_functions_with_c_function_pointers.html
Re[13]: Метод класс как С колбек
От: Evgeny.Panasyuk Россия  
Дата: 10.04.15 19:44
Оценка:
Здравствуйте, beyv, Вы писали:

EP>>>>И в чём там принципиальное отличие от std::function в рамках вопроса ТС?

B>>>FastDelegate инкапсулирует в себе и "this" и "метод класса"
EP>>Точно также как и std::function + std::bind, только проблему ТС это никак не решает
B>Ну вы даете, я вам нарисовал как сделать делегата(колбек) на экземпляр любого класса и любой его метод(хоть с 6 параметрами),
B>как передать его в "void addCallback(void * internalState, Callback * callback);" вместо "callback"

Этот callback будет использоваться внутри именно как указатель на функцию (то есть будет совершён вызов кода по этому адресу), вместо которого ты пытаешься передать указатель на экземпляр класса FastDelegate.
То есть получается что-то типа: LIVE DEMO
Re[9]: Метод класс как С колбек
От: BulatZiganshin  
Дата: 12.04.15 21:42
Оценка: +5
Здравствуйте, beyv, Вы писали:

B>Основная загвоздка у ТС получения доступа к полям класса в теле колбека или где там.


нет, у него классическая проблема — имеющийся код принимает указатель на сишную функцию и вызывает её именно как сишную функцию. если ты вместо этого передашь адрес класса, то естественно ничего не выйдет

и пож-та не хами, даже если ты полностью уверен в своей правоте
Люди, я люблю вас! Будьте бдительны!!!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.