2015-12-20 4 views
1

Мне нужно зарегистрировать таблицу указателей функций с некоторым API. Скажем, API выглядит следующим образом:Использование класса друга для скрытия частных статических методов обратного вызова

void (*FuncPtr)(void*); 
void RegisterCallbacks(FuncPtr const (&callbacks)[10], void* context); 

Поскольку я пишу код объектно-ориентированного программирования, создать статические «стуком» методы в моем классе, что в методах очередь вызовов на объект моего класса. Я использую указатель context, чтобы передать указатель на мой объект. Я хочу статически выделить всю таблицу указателей обратного вызова при статическом времени инициализации. Так что я в конечном итоге с этим:

class MyClass 
{ 
public: 

    static void Thunk (void* context) 
    { 
     reinterpret_cast<MyClass*>(context)->Method(); 
    } 

    void Method(); 

    // 
    // More callback thunks and methods... 
    // 

    MyClass() 
    { 
     RegisterCallbacks(s_callbackTable, this); 
    } 

private: 

    static const FuncPtr s_callbackTable[]; 
} 

// 
// In the .cpp file: 
// 
const FuncPtr MyClass::s_callbackTable[] = 
{ 
    &MyClass::Thunk, 
    ... 
}; 

Проблема с этим подходом является то, что мне нужно сделать MyClass::Thunk публично видимым, так что статическая инициализация может получить доступ к нему.

Редактировать: Я ошибся. По-видимому, вам не нужно публиковать это.

Я бы предпочел не, хотя. Я могу думать о двух способах решения этой проблемы.

Какой подход лучше?

  1. Создать частный статический метод, и сделать таблицу CallBack статической переменной внутри метода. Таким образом, я могу инициализировать его с помощью указателей на частные методы. Вызовите частный статический метод изнутри конструктора, чтобы получить таблицу.

  2. Создайте отдельный класс, содержащий общедоступные статические методы thunk и сделайте этот класс другом MyClass. Вперед объявите этот класс в заголовке для MyClass, чтобы разрешить объявление друга. Мне это нравится, потому что он также полностью скрывает методы thunk от любых внешних потребителей. Не совсем уверен, есть ли для этого ощутимая польза.

    Как так:

    // .h file: 
    
    class MyClassInternals 
    
    class MyClass 
    { 
    public: 
    
        void Method(); 
    
    private: 
    
        friend class MyClassInternals; 
    
        static const FuncPtr s_callbackTable[]; 
    } 
    
    // .cpp file: 
    
    class MyClassInternals 
    { 
    public: 
    
        static void Thunk(void* context) 
        { 
         reinterpret_cast<MyClass*>(context)->Method(); 
        } 
    } 
    
    const FuncPtr MyClass::s_callbackTable[] = 
    { 
        &MyClassInternals::Thunk, 
        ... 
    }; 
    

ответ

2

Вы можете просто держать все батут техники внутри реализации конструктора (.cpp), нет необходимости подвергать ничего о трамплинах на уровне интерфейса (.h):

MyClass::MyClass() { 
    struct trampolines { 
     static void method1(void *ctx) { ((MyClass *)ctx)->method1(); } 
     static void method2(void *ctx) { ((MyClass *)ctx)->method2(); } 
    }; 
    static void (*callbacks[])(void*) = { trampolines::method1, 
              trampolines::method2 }; 
    register_callbacks(callbacks, this); 
} 

Вам даже не нужно включать в (.h) заголовок библиотеки C, которая использует интерфейс обратного вызова/контекста. Это чище, так как клиенты вашего модуля не будут зависеть от данных C.

+0

Методы даже не должны быть общедоступными.Структура 'trampolines', благодаря тому, что она определена в рамках« MyClass », наследует ту же семантику, что и внутренний класс, то есть она может обращаться к приватным методам содержащего класса. См. Http://ideone.com/Ai7WW7. Также см. Http://stackoverflow.com/a/5714720/179895, который делает то же предложение, что и вы. – TripShock

+0

@TripShock: Спасибо за разъяснение. Я не большой сторонник 'private' /' protected' и не знал об этой тонкости локальных классов внутри области конструктора/метода. Исправлена. – 6502

1

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

После исправления нескольких незначительных синтаксических ошибок ваш исходный пример компилируется просто отлично, если Thunk - private.

См https://ideone.com/VmSasp

+0

Это странно ... что, черт возьми, я делаю: P – TripShock

+0

Есть ли какое-то значение в том, чтобы полностью скрыть частные методы thunk из интерфейса 'MyClass'? – TripShock

Смежные вопросы