Строгие псевдонимы и обратные вызовы с пользовательскими данными

У меня есть библиотека С++ с C API на основе обратного вызова. Тип обратного вызова выглядит следующим образом:

typedef struct {...} Result;
typedef void (*callback) (const Result* result, void* userData);

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

Моя главная забота здесь: нарушает ли это строгое правило псевдонимов или нет? Нужно ли менять тип userData на char*?


person tr3w    schedule 12.06.2012    source источник
comment
Зарегистрировать обратный вызов, например, как?   -  person jxh    schedule 12.06.2012
comment
Почему это нарушит строгое правило псевдонимов? Вы столкнулись с какой-либо ошибкой компилятора? Какой ? Какой компилятор?   -  person kebs    schedule 12.06.2012
comment
@kebs: отсутствия ошибки компилятора недостаточно, чтобы установить отсутствие строгого нарушения псевдонимов. Спрашивающий несколько неправильно понял правила, вот и все, и опасается, что в коде может быть невыявленная причина НБ. Спросить об этом кажется мудрым.   -  person Steve Jessop    schedule 12.06.2012
comment
@SteveJessop: Да, я знаю, что компилятор может выполнить катастрофическую оптимизацию, если мы не будем следовать правилам, но было неясно, какие предположения делает компилятор. У меня была глупая мысль, что он может каким-то образом отслеживать указатели, и я боялся, что эта цепочка порвется с приведением void*... Но теперь, кажется, я понял, как это работает.   -  person tr3w    schedule 12.06.2012
comment
@kebs: На самом деле я только что написал небольшой код (без обратных вызовов), который ломается на основе существования флага gcc -fno-strict-aliasing, и в любом случае компилируется без предупреждения даже с -Wall -Wstrict-aliasing=2 ...   -  person tr3w    schedule 12.06.2012


Ответы (2)


Нет, псевдонимы применяются только тогда, когда указатели являются косвенными, а не когда они передаются как значения указателя. Пока пользователь ведет себя последовательно, все будет в порядке.

То есть пользователь должен убедиться, что если он передает userData фактического типа T, то он всегда приводит его к T * в функции обратного вызова перед доступом к нему.

Я предполагаю, конечно, что вы не косвенно указываете userData в своей библиотеке, хотя доступ к нему снова как к указателю (например, printf("DEBUG: %p", userData)) в порядке.

person ecatmur    schedule 12.06.2012

Вы можете создать интерфейс, более похожий на C++, для API обратного вызова C, который может устранить вашу проблему с псевдонимами.

struct Callback {
    void (*callback) (Callback *cb_this, const Result *result);
};

Программист на C, использующий ваш API обратного вызова C для регистрации обратного вызова, сделал бы что-то вроде этого:

struct MyCallback {
    struct Callback cb;
    int x;
    double y;
    /* ... */
};

void my_callback_func (Callback *cb_this, const Result *result) {
    MyCallback *my_cb = (MyCallback *)cb_this;
    /* ... */
}

MyCallback mycb = { { my_callback_func }, 0, 0, /* ... */ };

Таким образом, вам не нужно беспокоиться об обработке void *. Вместо этого вам передается один указатель на Callback.

person jxh    schedule 12.06.2012
comment
@SteveJessop: нет сомнений, что это плохо для C++, но OP специально сказал, что это API обратного вызова C, реализуемый на C++. - person jxh; 12.06.2012
comment
@SteveJessop: я уточнил ответ, указав, что это для программиста C, использующего API обратного вызова. Спасибо и привет. - person jxh; 12.06.2012
comment
А, теперь понял, спасибо. Я почему-то подумал, что вы говорите о пользователях C++ C-подобного API. - person Steve Jessop; 12.06.2012