2015-04-28 10 views
4

Я использую API, который принимает только недействительные функции обратного вызова:Как избежать повторения функций обратного вызова (C++)

void (* CALLBACKFUNC) (void);

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

void myFunc (int a);

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

event1 -> calling myFunc(1);
event2 -> calling myFunc(2);
...

Число событий ограниченный и MAX предопределен (если это помогает), но я не хочу реплицировать функциональность (фактически, в реальном случае есть несколько входных значений и репликация вызова функции fo r различные комбинации - непростая задача)

P.S: Я также могу использовать C++ 11. Какие-либо предложения?.

+1

Что вы пытаетесь? Пожалуйста, покажите нам некоторый (релевантный) код. И если у вас есть ошибки, пожалуйста, покажите их. –

+1

Я попытался передать функтор вместо указателя функции, который, очевидно, получил ошибку: невозможно преобразовать аргумент x из 'FUNCTOR_TYPE' в 'CALLBACKFUNC' –

+0

Вы можете использовать 'std :: function' и' std :: bind' для настраиваемые функции обратного вызова. Это функции C++ 11. –

ответ

2

Если вам нужно пройти void (* CALLBACKFUNC) (void), и это не может быть изменено, вы не сможете передать функции состояния, период. Тем не менее, вы можете поделиться функциональностью:

void myFunc (int a) { 
    std::cout << a; 
} 
void myFunc1() {myFunc(1);} 
void myFunc2() {myFunc(2);} 
void myFunc3() {myFunc(3);} 
void myFunc4() {myFunc(4);} 
void myFunc5() {myFunc(5);} 

int main() { 
    use_callback(myFunc1); 
    use_callback(myFunc2); 
    use_callback(myFunc3); 
    use_callback(myFunc4); 
    use_callback(myFunc5); 
} 

Если это не работает для вас, то вам необходимо перестроить свой код, и/или использовать глобал передать состояние вокруг. Если вы абсолютно должны, и я НЕ рекомендую это, вы можете использовать массив обратных вызовов и дать каждому свое собственное глобальное состояние.

struct callback_state { 
    int p; 
}; 
struct callback_meta { 
    bool is_used = false; 
    CALLBACKFUNC func; 
    callback_state state {}; 
}; 
static const size_t max_callbacks = 6; 
callback_meta meta_array[max_callbacks]; 
lock_type global_callback_lock; //of course you'll need a lock 
#define GLUE2(X,Y) X##Y 
#define GLUE(X,Y) GLUE2(X,Y) 
#define DEFINE_CALLBACK(X) void GLUE(func,X) { \ 
     real_func(meta_array[X].state.p); \ 
     globcal_callback_lock.lock(); \ 
     meta_array[i].is_used = false; \ 
     globcal_callback_lock.unlock(); \ 
    } \ 
    meta_array[X].func = GLUE(func,X); 

CALLBACKFUNC prepare_callback(callback_state state) { 
    CALLBACKFUNC ret = 0; 
    globcal_callback_lock.lock(); 
    for(int i=0; i<max_callbacks; ++i) { 
     if(meta_array[i].is_used == false) { 
      meta_array[i].state = state; 
      meta_array[i].is_used = true; 
      ret = meta_array[i].func; 
      break; 
     } 
    } 
    globcal_callback_lock.unlock();   
    return ret; 
} 
DEFINE_CALLBACK(0); //boost preprocessor would go a long way here 
DEFINE_CALLBACK(1); 
DEFINE_CALLBACK(2); 
DEFINE_CALLBACK(3); 
DEFINE_CALLBACK(4); 
DEFINE_CALLBACK(5); 
+0

Большое спасибо @Mooing Duck! Я действительно поражен вашей скоростью мышления и кодирования! Я могу принять только один ответ! Думаю, мне нужно пойти на ответ Краба. –

4

Что касается этого решения? Вам не нужно определять вручную новую функцию для установки разных состояний.

#include <iostream> 

void setState(int s) { 

    std::cout << "Set state to " << s << std::endl; 

} 

template <int n> 
void myWrapper() { 
    setState(n); 
} 


void myLogic(void(*CALLBACK)(void)) { 

    CALLBACK(); 
} 


int main(int argc, char* argv[]) { 


    myLogic(myWrapper<50>); 
    myLogic(myWrapper<100>); 

} 
+0

Хорошо, я принял это решение, но я не знал, что во время компиляции должны быть известны «аргументы шаблона без типов». Я не понимаю, как это может быть полезно. Я имею в виду, если не могу передать аргумент оболочке, это не поможет. правильно? –

+1

@TOWI_Parallelism: это правда, поэтому не соглашайтесь с этим, поэтому вопрос рассматривается другим ppl как не ответил. Я подумаю об этом. – Krab

1

Существует одно решение, но это больше похоже на хак, и это не зависит от платформы . Он будет работать только на 32-битной архитектуре x86 , когда используется соглашение о вызове cdecl.

#include <iostream> 

const int PARAMS = 0xCAFEBABE; // set this value to be something unique (read below) 

typedef void(*CALLBACK)(void); 

void functionICantModify(CALLBACK cb) { 

    cb(); 

} 

/* 
* It depends on how on x86 with cdecl calling convention the stack frame 
* is generated by most compilers. 
* 
* 
* Stack frame when called as myCallback(); 
*  <--- esp is now same as ebp and points to old ebp on stack (mov ebp, esp) 
* [ebp] ; this is old ebp (push ebp) 
* [returnAddress] ; return address to functionICantModify 
* [undefined] ; possibly local variable of functionICantModify or old ebp 
* 
* 
* Stack frame when called as myCallback(PARAMS, 10, 50) 
*  <--- esp is now same as ebp and points to old ebp on stack (mov ebp, esp) 
* [ebp] ; this is old ebp (push ebp) 
* [returnAddress] ; return address to function from where i called myCallback(PARAMS, ...) 
* [PARAMS] ; this is our flag 
* [param1] 
* [param2] 
* 
* 
* ebp register acts like a frame pointer. 
* When access to local variables, ebp is substracted (first local 32bit variable is [ebp-4], second [ebp-8], ...) 
* When access to function arguments, ebp is added (first 32bit argument is [ebp+8], because [ebp] points to old ebp and [ebp+4] is return address (on 32bit architecture) 
* 
* You can see from those stack frames abowe that when you will access the paramsFlag in myCallback when 
* myCallback called from functionICantModify, you will get that undefined value. So it is important 
* to set the PARAMS value to be something unique that will never appears on stack in that place when 
* myCallback is called. By checking paramsFlag, you can detect if the function is called by 
* functionICantModify or by your logic. 
*/ 
void myCallback(int paramsFlag, int param1, int param2) { 

    /* 
    * This is how prolog is generated by most compilers on x86 (intel syntax) 
    * push ebp 
    * mov ebp, esp 
    */ 
    static int _param1 = 0; 
    static int _param2 = 0; 

    if (paramsFlag == PARAMS) { // called from my logic, setup state 
     std::cout << "Called to set state"; 
     _param1 = param1; 
     _param2 = param2; 
    } 
    else { // called from functionICantModify, don't access the function parameters !!!!!!!!!!!!! 
     std::cout << "Called from functionICantModify, _param1=" << _param1 << ", _param2=" << _param2 << std::endl; 
    } 

    /* 
    * This is how epilog is generated by most compilers on x86 with cdecl (intel syntax) 
    * mov esp, ebp 
    * pop ebp 
    * ret 
    */ 
} 

int main(int argc, char* argv[]) { 

    myCallback(PARAMS, 10, 50); 
    functionICantModify((CALLBACK)myCallback); 

} 
1

Заслуга Краб за указание не-типа temoplate аргументы. Давайте сыграем в игру с компилятором. Если ему не нравятся переменные времени выполнения, дайте ему один во время компиляции: указатель.

void setState(int s) { 
    printf("a is: %d\n", s); 
} 

template <int *n> 
void myWrapperss() { 
    setState(*n); 
} 

int a; 

void myLogic(void(*CALLBACK)(void)) { 
    CALLBACK(); 
} 

int main(int argc, char* argv[]) { 

    a = 3; 
    myLogic(myWrapperss<&a>); 

} 
0

Это мое решение :-) У него есть ограничения, конечно. Но эти ограничения не должны иметь большого значения для вашего случая, когда количество событий известно во время компиляции. Этот подход позволяет использовать состояния выполнения для обратных вызовов.

#include <iostream> 

template <int id> 
class StatefulCallback { 
public: 
    typedef void Callback(); 
    static Callback* get(int v) { 
    s_state_ = v; 
    return callback; 
    } 

private: 
    static int s_state_; 
    static void callback() { 
    std::cout << s_state_ << '\n'; 
    } 
}; 

template <int id> 
int StatefulCallback<id>::s_state_ = 0; 

int main() { 
    const int MAX = 10; 

    auto callback_for_event1 = StatefulCallback<1>::get(7); 
    auto callback_for_event2 = StatefulCallback<2>::get(9); 
    // ... 
    auto callback_for_eventMAX = StatefulCallback<MAX>::get(3); 

    callback_for_event1(); 
    callback_for_event2(); 
    // ... 
    callback_for_eventMAX(); 
} 
Смежные вопросы