Как передать функцию-член класса в качестве обратного вызова?

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

Вот что я сделал из своего конструктора:

m_cRedundencyManager->Init(this->RedundencyManagerCallBack);

Это не компилируется - появляется следующая ошибка:

Ошибка 8, ошибка C3867: «CLoggersInfra :: RedundencyManagerCallBack»: отсутствует список аргументов при вызове функции; используйте & CLoggersInfra :: RedundencyManagerCallBack для создания указателя на член

Попробовал использовать &CLoggersInfra::RedundencyManagerCallBack - у меня не сработало.

Любые предложения / объяснения по этому поводу ??

Я использую VS2008.

Спасибо!!


person ofer    schedule 30.12.2008    source источник


Ответы (12)


Это не работает, потому что указатель на функцию-член не может обрабатываться как обычный указатель на функцию, поскольку он ожидает аргумента объекта «this».

Вместо этого вы можете передать статическую функцию-член следующим образом, которые в этом отношении похожи на обычные функции, не являющиеся членами:

m_cRedundencyManager->Init(&CLoggersInfra::Callback, this);

Функцию можно определить следующим образом

static void Callback(int other_arg, void * this_pointer) {
    CLoggersInfra * self = static_cast<CLoggersInfra*>(this_pointer);
    self->RedundencyManagerCallBack(other_arg);
}
person Johannes Schaub - litb    schedule 30.12.2008
comment
Хотя это может быть решение / обходной путь ДЛЯ ОП, я не понимаю, как это является ответом на фактический вопрос. - person Stefan Steiger; 27.03.2019
comment
@StefanSteiger ответ (объяснение) находится в последнем абзаце (по сути: указатель функции-члена не может обрабатываться как указатель на бесплатную функцию), а предложение, что делать еще, находится в других частях моего ответа. Это правда, что это могло быть более подробно. Но это нормально, и именно поэтому мой ответ не получил столько голосов, сколько другие. Иногда более сжатые ответы, которые по сути содержат только код, нуждаются в помощи лучше, чем более длинные, и поэтому мой ответ был принят. - person Johannes Schaub - litb; 27.03.2019
comment
Шауб: Да, именно моя точка зрения. Но я вижу - вы должны были сначала написать последнюю часть, а потом сказать: вместо этого вы можете сделать это + (первая часть) - person Stefan Steiger; 27.03.2019
comment
Это не работает, если вы хотите, чтобы функция-член вызывалась из другого класса (по крайней мере, я не могу понять, как это сделать ...). - person dmedine; 05.02.2021

Это простой вопрос, но ответ на удивление сложен. Короткий ответ: вы можете делать то, что пытаетесь сделать, с std::bind1st или boost::bind. Более подробный ответ ниже.

Компилятор правильно предлагает вам использовать &CLoggersInfra::RedundencyManagerCallBack. Во-первых, если RedundencyManagerCallBack является функцией-членом, сама функция не принадлежит какому-либо конкретному экземпляру класса CLoggersInfra. Он принадлежит к самому классу. Если вы когда-либо раньше вызывали функцию статического класса, возможно, вы заметили, что используете тот же синтаксис SomeClass::SomeMemberFunction. Поскольку сама функция является «статической» в том смысле, что она принадлежит классу, а не конкретному экземпляру, вы используете тот же синтаксис. Знак «&» необходим, потому что с технической точки зрения вы не передаете функции напрямую - функции не являются реальными объектами в C ++. Вместо этого вы технически передаете адрес памяти для функции, то есть указатель на то, где инструкции функции начинаются в памяти. Однако последствия такие же: вы фактически «передаете функцию» в качестве параметра.

Но в данном случае это только половина проблемы. Как я уже сказал, RedundencyManagerCallBack функция не «принадлежит» какому-либо конкретному экземпляру. Но похоже, что вы хотите передать его как обратный вызов, имея в виду конкретный экземпляр. Чтобы понять, как это сделать, вам необходимо понять, что такое функции-члены на самом деле: обычные функции, не определенные в каком-либо классе, с дополнительным скрытым параметром.

Например:

class A {
public:
    A() : data(0) {}
    void foo(int addToData) { this->data += addToData; }

    int data;
};

...

A an_a_object;
an_a_object.foo(5);
A::foo(&an_a_object, 5); // This is the same as the line above!
std::cout << an_a_object.data; // Prints 10!

Сколько параметров принимает A::foo? Обычно мы бы сказали 1. Но под капотом foo действительно занимает 2. Глядя на определение A :: foo, ему нужен конкретный экземпляр A, чтобы указатель this имел смысл (компилятор должен знать, что) это). Обычно вы указываете, что вы хотите, чтобы это было через синтаксис MyObject.MyMemberFunction(). Но это всего лишь синтаксический сахар для передачи адреса MyObject в качестве первого параметра в MyMemberFunction. Точно так же, когда мы объявляем функции-члены внутри определений классов, мы не помещаем «this» в список параметров, но это просто подарок дизайнеров языка для экономии ввода. Вместо этого вы должны указать, что функция-член является статической, чтобы отказаться от нее, автоматически получив дополнительный параметр this. Если компилятор C ++ переведет приведенный выше пример в код C (исходный компилятор C ++ действительно работал таким образом), он, вероятно, напишет что-то вроде этого:

struct A {
    int data;
};

void a_init(A* to_init)
{
    to_init->data = 0;
}

void a_foo(A* this, int addToData)
{ 
    this->data += addToData;
}

...

A an_a_object;
a_init(0); // Before constructor call was implicit
a_foo(&an_a_object, 5); // Used to be an_a_object.foo(5);

Возвращаясь к вашему примеру, теперь есть очевидная проблема. «Init» нужен указатель на функцию, которая принимает один параметр. Но &CLoggersInfra::RedundencyManagerCallBack - это указатель на функцию, которая принимает два параметра: обычный параметр и секретный параметр this. Вот почему вы все еще получаете ошибку компилятора (в качестве побочного примечания: если вы когда-либо использовали Python, такая путаница связана с тем, почему для всех функций-членов требуется параметр self).

Подробный способ справиться с этим - создать специальный объект, который содержит указатель на нужный вам экземпляр и имеет функцию-член, называемую чем-то вроде 'run' или 'execute' (или перегружает оператор '()'), который принимает параметры для функции-члена и просто вызывает функцию-член с этими параметрами в сохраненном экземпляре. Но для этого вам потребуется изменить «Init», чтобы он принимал ваш особый объект, а не необработанный указатель на функцию, и похоже, что Init - это чей-то другой код. А создание специального класса для каждого случая, когда возникает эта проблема, приведет к раздуванию кода.

Итак, теперь, наконец, хорошее решение, boost::bind и boost::function, документацию по каждому из которых вы можете найти здесь:

boost :: bind docs, boost :: function docs

boost::bind позволит вам взять функцию и параметр для этой функции и создать новую функцию, в которой этот параметр «заблокирован» на месте. Итак, если у меня есть функция, которая добавляет два целых числа, я могу использовать boost::bind, чтобы создать новую функцию, в которой один из параметров заблокирован, чтобы сказать 5. Эта новая функция будет принимать только один целочисленный параметр и всегда будет добавлять к нему 5 специально. Используя этот метод, вы можете заблокировать скрытый параметр this как конкретный экземпляр класса и сгенерировать новую функцию, которая принимает только один параметр, как вы хотите (обратите внимание, что скрытым параметром всегда является первый параметр, а обычные параметры идут по порядку после него). Посмотрите boost::bind документы для примеров, они даже специально обсуждают его использование для функций-членов. Технически существует стандартная функция под названием [std::bind1st][3], которую вы также можете использовать, но boost::bind является более общей.

Конечно, есть еще одна загвоздка. boost::bind сделает для вас хороший boost :: function, но технически это все еще не простой указатель на функцию, как, вероятно, хочет Init. К счастью, boost предоставляет способ преобразования boost :: function в необработанные указатели, как описано в StackOverflow здесь. Как это реализуется, выходит за рамки этого ответа, хотя это тоже интересно.

Не волнуйтесь, если это кажется смехотворно сложным - ваш вопрос пересекает несколько темных углов C ++, и boost::bind станет невероятно полезным, когда вы его изучите.

Обновление C ++ 11: вместо boost::bind теперь вы можете использовать лямбда-функцию, которая фиксирует this. По сути, компилятор генерирует для вас то же самое.

person Joseph Garvin    schedule 31.12.2008
comment
tldr. Черт возьми! Надо набрать хотя бы 15 символов ?? - person Ogre Psalm33; 13.04.2012
comment
Это отличный ответ! - person aardvarkk; 31.10.2013
comment
В начале ответа предлагается std :: bind1st как способ реализации решения, но последняя часть ответа касается исключительно boost :: bind. Как можно использовать std :: bind1st? - person mabraham; 01.06.2014
comment
@mabraham std::bind1st устарел в C ++ 17, поэтому его не следует использовать. std::bind используется почти так же, как boost::bind: stackoverflow.com/a/10555796/4566599 - person Roi Danton; 28.07.2017
comment
@Roi Danton Спасибо, но до сих пор нет примера кода, подходящего для любого не-Boost std-контекста ... - person mabraham; 29.07.2017
comment
@mabraham Хорошо, я добавил быстрый пример, хотя он не полностью соответствует вопросу (VS2008): stackoverflow.com/a/45525074 / 4566599 - person Roi Danton; 05.08.2017
comment
Это должен быть принятый ответ! Принятый ответ на самом деле не работает, если вы не можете изменить библиотеку или передать необязательные аргументы. - person Carson McNeil; 12.08.2017
comment
@ Джозеф Гарвин: Не могу понять, как std :: bind - это ответ. для этого требуется, чтобы аргумент имел тип std :: function вместо обычного указателя на функцию C. просто потому, что вы скрываете передачу this, это не лучше, чем принятый ответ. Хорошо, хорошо, если у вас есть доступ на уровне исходного кода к рассматриваемой сигнатуре функции, вы можете изменить foo * на std :: function ‹foo_signature›, а затем вам нужно только изменить это, предполагая, что все компиляторы обновлены до C ++ 11 , но если у вас нет доступа к исходному тексту, то вы F * ED, потому что подписи несовместимы. Это то же самое, что и лямбда-выражение C ++. - person Stefan Steiger; 27.03.2019
comment
У меня это не работает, пример кода не компилируется с ошибкой в ​​строке A::foo(&an_a_object, 5);: нет функции соответствия, кандидат ожидает 1 аргумент. Было ли это взломом более ранних стандартов, которые с тех пор были удалены? Я использую C ++ 11 - person Apollys supports Monica; 13.09.2019
comment
Clang также выдает ошибку на той же полосе error: call to non-static member function without an object argument. - person Apollys supports Monica; 14.09.2019
comment
@Apollys: То, что я сказал, верно по духу / концепции, но технически C ++ действительно считает функции-члены отличными от обычных функций и имеет для них отдельный синтаксис. На некоторых платформах ABI для вызова члена и не члена отличается, а не просто дополнительным аргументом в исходном ABI, поэтому компилятор должен знать, с чем он имеет дело (указатель на бесплатную функцию против указателя на функцию-член). Синтаксис здесь: isocpp.org/wiki/faq/pointers-to-members . Увеличьте привязку и лямбды, запечатлевшие эту бумагу, поверх этой детали для вас. - person Joseph Garvin; 14.09.2019
comment
Тогда я не понимаю, в чем суть всего этого. Я перешел по вашей последней ссылке и не вижу способа напрямую преобразовать boost::function в указатель на функцию, вам нужно обернуть его. Если нам нужно обернуть его, то не можем ли мы просто выполнить обратный вызов с телом static_cast<SpecificClass*>(context_ptr)->MemberFunction();? - person Apollys supports Monica; 17.09.2019
comment
@Apollys прошло много лет с тех пор, как мне пришлось использовать функцию повышения, но я предполагаю, что если вы используете функцию повышения для обертывания функции-члена без привязки конкретного экземпляра объекта, у него все равно будет метод, позволяющий получить обычный указатель на функцию , но сигнатура этого указателя на тип функции будет иметь список аргументов с дополнительным первым аргументом - экземпляром объекта. Внутренне функция, не являющаяся членом, она будет сгенерирована (с использованием шаблонов) и предоставит вам адрес, который будет использовать синтаксис вызова указателя функции члена (указатель функции члена будет аргументом шаблона). - person Joseph Garvin; 17.09.2019
comment
@Apollys, другими словами, функция boost заставляет вас использовать функции батутов, чтобы скрыть различия в типах указателей. - person Joseph Garvin; 17.09.2019

Этот ответ является ответом на комментарий выше и не работает с VisualStudio 2008, но его следует предпочесть более поздним компиляторам.


Между тем вам больше не нужно использовать указатель void, и нет необходимости в повышении, поскольку std::bind и std::function доступны. Одно преимущество (по сравнению с указателями void) - безопасность типов, поскольку тип возвращаемого значения и аргументы явно указываются с использованием std::function:

// std::function<return_type(list of argument_type(s))>
void Init(std::function<void(void)> f);

Затем вы можете создать указатель на функцию с помощью std::bind и передать его в Init:

auto cLoggersInfraInstance = CLoggersInfra();
auto callback = std::bind(&CLoggersInfra::RedundencyManagerCallBack, cLoggersInfraInstance);
Init(callback);

Полный пример использования std::bind с функциями-членами, статическими элементами и функциями, не являющимися членами:

#include <functional>
#include <iostream>
#include <string>

class RedundencyManager // incl. Typo ;-)
{
public:
    // std::function<return_type(list of argument_type(s))>
    std::string Init(std::function<std::string(void)> f) 
    {
        return f();
    }
};

class CLoggersInfra
{
private:
    std::string member = "Hello from non static member callback!";

public:
    static std::string RedundencyManagerCallBack()
    {
        return "Hello from static member callback!";
    }

    std::string NonStaticRedundencyManagerCallBack()
    {
        return member;
    }
};

std::string NonMemberCallBack()
{
    return "Hello from non member function!";
}

int main()
{
    auto instance = RedundencyManager();

    auto callback1 = std::bind(&NonMemberCallBack);
    std::cout << instance.Init(callback1) << "\n";

    // Similar to non member function.
    auto callback2 = std::bind(&CLoggersInfra::RedundencyManagerCallBack);
    std::cout << instance.Init(callback2) << "\n";

    // Class instance is passed to std::bind as second argument.
    // (heed that I call the constructor of CLoggersInfra)
    auto callback3 = std::bind(&CLoggersInfra::NonStaticRedundencyManagerCallBack,
                               CLoggersInfra()); 
    std::cout << instance.Init(callback3) << "\n";
}

Возможный выход:

Hello from non member function!
Hello from static member callback!
Hello from non static member callback!

Кроме того, с помощью std::placeholders вы можете динамически передавать аргументы обратному вызову (например, это позволяет использование return f("MyString"); в Init, если f имеет строковый параметр).

person Roi Danton    schedule 05.08.2017
comment
Настоящее, очень большое спасибо от меня за этот ответ! Я потратил уже более двух часов на поиск и пробу разных подходов, но ничего не помогло. Но этот настолько простой, что сработал уже через 1 минуту. - person BadK; 02.10.2018
comment
Разве это не требует изменения RedundancyManager кода, к которому OP не имеет доступа? - person simplename; 23.05.2021

Какой аргумент принимает Init? Что за новое сообщение об ошибке?

Указатели методов в C ++ немного сложны в использовании. Помимо самого указателя метода, вам также необходимо предоставить указатель экземпляра (в вашем случае this). Может Init ожидает этого как отдельного аргумента?

person Konrad Rudolph    schedule 30.12.2008

Указатель на функцию-член класса - это не то же самое, что указатель на функцию. Член класса принимает неявный дополнительный аргумент (указатель this) и использует другое соглашение о вызовах.

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

person jalf    schedule 30.12.2008

m_cRedundencyManager может использовать функции-члены? Большинство обратных вызовов настроены на использование обычных функций или статических функций-членов. Взгляните на эту страницу в C ++ FAQ Lite Чтобы получить больше информации.

Обновление: предоставленное вами объявление функции показывает, что m_cRedundencyManager ожидает функцию вида: void yourCallbackFunction(int, void *). Следовательно, в этом случае функции-члены неприемлемы в качестве обратных вызовов. Статическая функция-член может работать, но если это неприемлемо в вашем случае, следующий код также будет работать. Обратите внимание, что он использует злое заклинание от void *.


// in your CLoggersInfra constructor:
m_cRedundencyManager->Init(myRedundencyManagerCallBackHandler, this);

// in your CLoggersInfra header:
void myRedundencyManagerCallBackHandler(int i, void * CLoggersInfraPtr);

// in your CLoggersInfra source file:
void myRedundencyManagerCallBackHandler(int i, void * CLoggersInfraPtr)
{
    ((CLoggersInfra *)CLoggersInfraPtr)->RedundencyManagerCallBack(i);
}
person e.James    schedule 30.12.2008

Некромантинг.
Я думаю, что на сегодняшний день ответы немного неясны.

Приведем пример:

Предположим, у вас есть массив пикселей (массив значений ARGB int8_t)

// A RGB image
int8_t* pixels = new int8_t[1024*768*4];

Теперь вы хотите сгенерировать PNG. Для этого вы вызываете функцию toJpeg

bool ok = toJpeg(writeByte, pixels, width, height);

где writeByte - это callback-функция

void writeByte(unsigned char oneByte)
{
    fputc(oneByte, output);
}

Проблема здесь: вывод FILE * должен быть глобальной переменной.
Очень плохо, если вы работаете в многопоточной среде (например, http-сервер).

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

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

class BadIdea {
private:
    FILE* m_stream;
public:
    BadIdea(FILE* stream)  {
        this->m_stream = stream;
    }

    void writeByte(unsigned char oneByte){
            fputc(oneByte, this->m_stream);
    }

};

А потом сделай

FILE *fp = fopen(filename, "wb");
BadIdea* foobar = new BadIdea(fp);

bool ok = TooJpeg::writeJpeg(foobar->writeByte, image, width, height);
delete foobar;
fflush(fp);
fclose(fp);

Однако вопреки ожиданиям это не работает.

Причина в том, что функции-члены C ++ реализованы как функции расширения C #.

Так что у тебя есть

class/struct BadIdea
{
    FILE* m_stream;
}

и

static class BadIdeaExtensions
{
    public static writeByte(this BadIdea instance, unsigned char oneByte)
    {
         fputc(oneByte, instance->m_stream);
    }

}

Поэтому, когда вы хотите вызвать writeByte, вам нужно передать не только адрес writeByte, но и адрес экземпляра BadIdea.

Итак, когда у вас есть typedef для процедуры writeByte, и он выглядит так

typedef void (*WRITE_ONE_BYTE)(unsigned char);

И у вас есть подпись writeJpeg, которая выглядит так

bool writeJpeg(WRITE_ONE_BYTE output, uint8_t* pixels, uint32_t 
 width, uint32_t height))
    { ... }

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

Следующее лучшее, что вы можете сделать в C ++, - это использовать лямбда-функцию:

FILE *fp = fopen(filename, "wb");
auto lambda = [fp](unsigned char oneByte) { fputc(oneByte, fp);  };
bool ok = TooJpeg::writeJpeg(lambda, image, width, height);

Однако, поскольку лямбда не делает ничего другого, кроме передачи экземпляра скрытому классу (например, классу «BadIdea»), вам необходимо изменить подпись writeJpeg.

Преимущество лямбда над ручным классом в том, что вам просто нужно изменить один typedef

typedef void (*WRITE_ONE_BYTE)(unsigned char);

to

using WRITE_ONE_BYTE = std::function<void(unsigned char)>; 

И тогда вы можете оставить все остальное нетронутым.

Вы также можете использовать std :: bind

auto f = std::bind(&BadIdea::writeByte, &foobar);

Но это за кулисами просто создает лямбда-функцию, которая затем также требует изменения typedef.

Так что нет способа передать функцию-член методу, который требует статического указателя на функцию.

Но лямбды - это простой способ, если у вас есть контроль над источником.
Иначе тебе не повезло.
С C ++ ничего не поделаешь.

Примечание.
std :: function требует #include <functional>

Однако, поскольку C ++ позволяет вам также использовать C, вы можете сделать это с помощью libffcall в простой C, если вы не против привязать зависимость.

Загрузите libffcall из GNU (по крайней мере, на ubuntu, не используйте предоставленный дистрибутивом пакет - он сломан), разархивируйте.

./configure
make
make install

gcc main.c -l:libffcall.a -o ma

main.c:

#include <callback.h>

// this is the closure function to be allocated 
void function (void* data, va_alist alist)
{
     int abc = va_arg_int(alist);

     printf("data: %08p\n", data); // hex 0x14 = 20
     printf("abc: %d\n", abc);

     // va_start_type(alist[, return_type]);
     // arg = va_arg_type(alist[, arg_type]);
     // va_return_type(alist[[, return_type], return_value]);

    // va_start_int(alist);
    // int r = 666;
    // va_return_int(alist, r);
}



int main(int argc, char* argv[])
{
    int in1 = 10;

    void * data = (void*) 20;
    void(*incrementer1)(int abc) = (void(*)()) alloc_callback(&function, data);
    // void(*incrementer1)() can have unlimited arguments, e.g. incrementer1(123,456);
    // void(*incrementer1)(int abc) starts to throw errors...
    incrementer1(123);
    // free_callback(callback);
    return EXIT_SUCCESS;
}

И если вы используете CMake, добавьте библиотеку компоновщика после add_executable

add_library(libffcall STATIC IMPORTED)
set_target_properties(libffcall PROPERTIES
        IMPORTED_LOCATION /usr/local/lib/libffcall.a)
target_link_libraries(BitmapLion libffcall)

или вы можете просто динамически связать libffcall

target_link_libraries(BitmapLion ffcall)

Примечание.
Вы можете включить заголовки и библиотеки libffcall или создать проект cmake с содержимым libffcall.

person Stefan Steiger    schedule 27.03.2019

Я вижу, что у init есть следующее переопределение:

Init(CALLBACK_FUNC_EX callback_func, void * callback_parm)

где CALLBACK_FUNC_EX

typedef void (*CALLBACK_FUNC_EX)(int, void *);
person ofer    schedule 30.12.2008

Простым решением "обходного пути" по-прежнему является создание класса виртуальных функций "interface" и наследование его в классе вызывающей стороны. Затем передайте его как параметр «может быть в конструкторе» другого класса, который вы хотите вызвать обратно в свой класс вызывающего.

ОПРЕДЕЛЕНИЕ Интерфейса:

class CallBack 
{
   virtual callMeBack () {};
};

Это класс, которому вы хотите перезвонить:

class AnotherClass ()
{
     public void RegisterMe(CallBack *callback)
     {
         m_callback = callback;
     }

     public void DoSomething ()
     {
        // DO STUFF
        // .....
        // then call
        if (m_callback) m_callback->callMeBack();
     }
     private CallBack *m_callback = NULL;
};

И это тот класс, который будет отозван.

class Caller : public CallBack
{
    void DoSomthing ()
    {
    }

    void callMeBack()
    {
       std::cout << "I got your message" << std::endl;
    }
};
person M.Hefny    schedule 03.11.2019

Этот вопрос и ответ из C ++ FAQ Lite, на мой взгляд, довольно хорошо освещает ваш вопрос и соображения, связанные с ответом. Короткий фрагмент веб-страницы, на которую я указал:

Не надо.

Поскольку функция-член бессмысленна без объекта для ее вызова, вы не можете сделать это напрямую (если система X Window была переписана на C ++, она, вероятно, передавала бы ссылки на окружающие объекты, а не только указатели на функции; естественно, объекты будет воплощать требуемую функцию и, возможно, многое другое).

person Onorio Catenacci    schedule 30.12.2008
comment
Ссылка теперь выглядит как isocpp.org/wiki/faq/pointers- участникам # memfnptr-vs-fnptr; похоже, теперь он говорит: «Не надо». Вот почему ответы только по ссылкам не годятся. - person lmat - Reinstate Monica; 12.01.2016
comment
Я изменил ответ @LimitedAtonement. Спасибо за указание на это. Вы совершенно правы, что ответы только по ссылкам - это ответы низкого качества. Но мы не знали этого еще в 2008 году :-P - person Onorio Catenacci; 13.01.2016

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

Кроме того, нестатическая функция-член имеет неявный / скрытый параметр для объекта. Указатель this неявно передается в качестве аргумента при вызове функции-члена. Таким образом, функции-члены могут быть вызваны только путем предоставления объекта.

Если API Init нельзя изменить, можно использовать функцию-оболочку (обычная функция или статическая функция-член класса), которая вызывает этот член. В худшем случае объект будет глобальным для доступа функции оболочки.

CLoggersInfra* pLoggerInfra;

RedundencyManagerCallBackWrapper(int val)
{
    pLoggerInfra->RedundencyManagerCallBack(val);
}
m_cRedundencyManager->Init(RedundencyManagerCallBackWrapper);

Если API Init можно изменить, существует множество альтернатив: указатель на нестатическую функцию-член объекта, объект функции, std::function или интерфейсная функция.

См. Сообщение в обратных вызовах для различных вариантов с Рабочие примеры C ++.

person ap-osd    schedule 08.05.2020

Похоже, std::mem_fn (C ++ 11) делает именно то, что вам нужно :

Шаблон функции std :: mem_fn генерирует объекты-оболочки для указателей на члены, которые могут сохранять, копировать и вызывать указатель на член. Как ссылки, так и указатели (включая интеллектуальные указатели) на объект могут использоваться при вызове std :: mem_fn.

person ben    schedule 03.06.2020