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));
Здравствуйте, AHOHUM, Вы писали:
AHO>В чём загвоздка?
В том что лямбда и бинд создают функциональные объекты. И использовать их может только код, который умеет работать с функциональными объектами. Так что ни как.
Что касается С колбек, то скорее всего в internalState есть место где хранить указатель на класс, вы его просто не нашли.
Здравствуйте, AHOHUM, Вы писали:
AHO>Имеется С библиотека с неизменяемым интерфейсом
AHO>В чём загвоздка?
std::bind возвращает совсем не Callback *, так что, если интерфейс библиотеки совсем не предполагает хранения указателей на некоторые пользовательские данные, то это придется делать самому. Например, можно держать в статическом поле класса ::std::map< /* internalState */ void *, Class * > и при вызове коллбэка находить соответствующий экземпляр класса.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Здравствуйте, 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;
}
//usagestruct MyTag1 {} ;// create any number of tags
//probably, we need to define static datavoid* 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
суть 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 (ссылку не могу найти)
успехов
Здравствуйте, fdn721, Вы писали:
F>Здравствуйте, AHOHUM, Вы писали:
AHO>>В чём загвоздка?
F>В том что лямбда и бинд создают функциональные объекты. И использовать их может только код, который умеет работать с функциональными объектами. Так что ни как.
F>Что касается С колбек, то скорее всего в internalState есть место где хранить указатель на класс, вы его просто не нашли.
Спасибо, понял. Внимательно поискал: ни в internalState, ни в data – нет места для сохранения указателя на объект класса. internalState имеет комментарий "internally used data" (и оно действительно так и есть, если более детально взглянуть на данную структуру), data содержит строго типизированные данные, которые ожидаются от библиотеки.
Здравствуйте, VTT, Вы писали:
VTT>Здравствуйте, AHOHUM, Вы писали:
AHO>>Имеется С библиотека с неизменяемым интерфейсом
AHO>>В чём загвоздка?
VTT>std::bind возвращает совсем не Callback *, так что, если интерфейс библиотеки совсем не предполагает хранения указателей на некоторые пользовательские данные, то это придется делать самому. Например, можно держать в статическом поле класса ::std::map< /* internalState */ void *, Class * > и при вызове коллбэка находить соответствующий экземпляр класса.
Спасибо, так и хотел сделать, но думал, что есть более "красивые" методы решения данной проблемы.
Здравствуйте, 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
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 году уже что-то есть в стандарте, что я мог запросто пропустить. В моём случае это будет из пушки по воробьям.
Спасибо за развернутый ответ, думаю, я определился с вариантом.
Здравствуйте, AHOHUM, Вы писали:
AHO>Не могу понять, возможно ли здесь воспользоваться лямбдой или bind(оба параметра: internalState, data — должны быть доступны):
есть пример как можно автоматически сохранить функциональный объект (лямбду/результат bind) в глобальный нумерованный объект и получить указатель на функцию. Например:
AHO>internalState – сугубо внутренняя структура библиотеки, data – данные получаемые из библиотеки, т.е. никаких void * userData нет.
1. Где создаётся и удаляется internalState? На нашей стороне?
2. В callback передаётся именно тот указатель internalState который был передан в addCallback?
I. Если на оба вопроса ответ да — то можно к нему прицепом нагрузить наши данные. То есть:
А callback будет доставать payload через offsetof.
II. Если ответ да только на второй вопрос — то можно создать отображение из *internalState в наши данные, например unordered_map<void*, OurData> — и callback будет знать из какой ячейки доставать payload.
AHO>internalState – сугубо внутренняя структура библиотеки, data – данные получаемые из библиотеки, т.е. никаких void * userData нет.
Ещё, как вариант (если другие не подходят), можно прятать указатель/индекс данных в padding'е internalState — если конечно его достаточно, и он нигде не теряется/не затирается.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, AHOHUM, Вы писали:
AHO>>Имеется С библиотека с неизменяемым интерфейсом, одна из функций выглядит так: AHO>>
AHO>>internalState – сугубо внутренняя структура библиотеки, data – данные получаемые из библиотеки, т.е. никаких void * userData нет.
EP>1. Где создаётся и удаляется internalState? На нашей стороне? EP>2. В callback передаётся именно тот указатель internalState который был передан в addCallback?
EP>I. Если на оба вопроса ответ да — то можно к нему прицепом нагрузить наши данные. То есть: EP>
А callback будет доставать payload через offsetof.
EP>II. Если ответ да только на второй вопрос — то можно создать отображение из *internalState в наши данные, например unordered_map<void*, OurData> — и callback будет знать из какой ячейки доставать payload.
1) Объявлен как поле класса, создается посредствам create(&internalState, …) этой же библиотеки.
2) Да, именно он.
Сейчас сделано как раз на unordered_map с ключом internalState, разбираюсь с 3м методом предложенным товарищем uzhas'ом.
Здравствуйте, sokel, Вы писали:
S>Здравствуйте, uzhas, Вы писали:
U>>3) зоопарк переменных можно зашаблонить
S>Если с тегами, то можно так примерно через std::function:
О, а в качестве тега можно тип внешней функций запользовать:
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, sokel, Вы писали:
U>>>3) зоопарк переменных можно зашаблонить S>>Если с тегами, то можно так примерно через std::function:
EP>std::function там не нужен.
std::funсtion зато не только thiscall, более универсальный.
EP>В качестве тэгов проще использовать числа чем типы. http://rsdn.ru/forum/cpp/5121566.1
Здравствуйте, sokel, Вы писали:
EP>>std::function там не нужен. S>std::funсtion зато не только thiscall, более универсальный.
Так я и не говорю что только thiscall — там может быть любой функциональный объект, что результат bind'а, что лямбда и т.п.
std::function нужен когда требуется стереть тип, а здесь же наоборот — чем конкретней тип, тем проще избежать коллизии.
EP>>В качестве тэгов проще использовать числа чем типы. http://rsdn.ru/forum/cpp/5121566.1
Одна проблема — это либо не компилируется / вылетает / либо статья старая, и автор не учитывает такую крутую штуку, как DEP.
А еще оно может съесть первый аргумент функции.
Не подскажет аль кто живой проект, и чтоб оно не съедало первый аргумент функции?
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>std::function нужен когда требуется стереть тип, а здесь же наоборот — чем конкретней тип, тем проще избежать коллизии.
Да, согласен. Просто быстрый вариант отложенного конструктора вместо optional
Инты можно легко в теги завернуть если что, ну а сигнатуру то всё равно нужно отдельным параметром передавать.
Кстати, optional не съест noncopyable. Можно свой holder сделать, без требования copy-constructible: