[С++] Передать метод в качестве аргумента.
Так метод - это же не совсем функция. В частности, как компилятор должен узнать, чьего объекта этот метод? Ты же с ним дальше собираешься работать не как с указателем, а как с объектом
Указатели на методы описываются как &Class::method, и это вовсе не указатели на функции.
ну можно использовать, например std::mem_fun* из хедера <functional>
mem_fun - это объект
но в отличие от указателя на фанкцию или функцию-член инкапсулирует именно то что нужно (т.е. объект к которому будет применяться метод)
PS точнее не объект, а шаблон
Конечно, так и надо сделать.
точнее надо bind1st(mem_fun(&T::f obj)
Может, имеет смысл сделать методы статическими?
Example
// functional_mem_fun.cpp
// compile with: /EHsc
#include <vector>
#include <functional>
#include <algorithm>
#include <iostream>
using namespace std;
class StoreVals
{
int val;
public:
StoreVals ( ) { val = 0; }
StoreVals ( int j ) { val = j; }
bool display ( ) { cout << val << " "; return true; }
int squareval ( ) { val *= val; return val; }
int lessconst ( int k ) {val -= k; return val; }
};
int main( )
{
vector <StoreVals *> v1;
v1.push_back( &StoreVals ( 5 ) );
v1.push_back( &StoreVals ( 10 ) );
v1.push_back( &StoreVals ( 15 ) );
v1.push_back( &StoreVals ( 20 ) );
v1.push_back( &StoreVals ( 25 ) );
cout << "The original values stored are: " ;
for_each( v1.begin( v1.end( mem_fun<bool, StoreVals> ( &StoreVals::display ) );
cout << endl;
// Use of mem_fun calling member function through a pointer
// square each value in the vector using squareval ( )
for_each( v1.begin( v1.end( mem_fun<int, StoreVals> ( &StoreVals::squareval ) );
cout << "The squared values are: " ;
for_each( v1.begin( v1.end( mem_fun<bool, StoreVals> ( &StoreVals::display ) );
cout << endl;
// Use of mem_fun1 calling member function through a pointer
// subtract 5 from each value in the vector using lessconst ( )
for_each( v1.begin( v1.end(
bind2nd ( mem_fun1<int, StoreVals,int> ( &StoreVals::lessconst 5 ) );
cout << "The squared values less 5 are: " ;
for_each( v1.begin( v1.end( mem_fun<bool, StoreVals> ( &StoreVals::display ) );
cout << endl;
}
Output
The original values stored are: 5 10 15 20 25
The squared values are: 25 100 225 400 625
The squared values less 5 are: 20 95 220 395 620
взято из мсдн
Ввиду отсутствия МСДНа и возможности его пока поставить, не мог бы ты хотя бы кратко сказать, как вообще работает эта функция mem_fun?
поищи стандарт Си++ или Страуструпа какого-нить в локалке. В общем, идея такая: та передаешь в функцию mem_fun один параметр: указатель на функцию-член, тебе возвращают объект для которого переопределена операция "круглые скобки", т.е. ты этот объект можешь использовать как функцию (т.н. функционал) с 2-мя аргументами: указатель на объект и аргумент твоего метода. Функция bind1st работает аналогично, у нее 2 параметра: бинарный функционал и значение для 1-го операнда, результат - унарный функционал (т.е. объект который можно использовать как функцию с 1-м параметром)
На самом деле так:
Если объявить метод статическим, проблемы действительно исчезнут. Но правда ли, что именно это тебе нужно, то есть правда ли, что этот метод не зависит от объекта.
В действительности, просто указатель на функцию нужен редко, функций в программе конечное число так что ему одному выразительности крайне не хватает. В Си как правило вместе с указателем на функцию передают "контекст", то есть некоторое void * которое эту функцию параметризует.
Например, вместо такой функции высшего порядка:
намного правильнее писать такую
typedef int (*fun_tint n);
int sum(fun_t f, int n1, int n2)
{
int s = 0;
for(int n=n1;n<n2;n++)
s+= f(n);
return(s);
}
Потому что в первом случае ты сможешь применить sum к таким функциям как f(x) = x+1, f(x) = x+2, но НЕ сможешь применить ее к f(x) = x+z, где z пользователь вводит с клавиатуры.
typedef int (*fun_tvoid * context, int n);
int sum(fun_t f, void * context, int n1, int n2)
{
int s = 0;
for(int n=n1;n<n2;n++)
s+= f(context, n);
return(s);
}
Чтобы прибавить эстетизма этому void * context, можно воспользоваться C++-ными member functions, но это довольно ущербно, потому что делегатов в С++ нет... Ну что-то это я уже умничать начинаю, того гляди тоже до template-ов дойду Лучше остановлюсь на том, что если ты не можешь объявить метод статическим, то сделай по такому принципу:
Я это все не компилировал, но на первый взгляд грамматических ошибок нет...
class C
{
int y;
public:
C(int y):y(y){}
int m(int x)
{
return(x+y);
}
}
int C_m(void * ths, int x)
{
return( C*)ths)->m(x) );
}
...
C c(5);
int s = sum(C_m, &c, 0, 10);
...
#include <iostream>
using namespace std;
typedef int (*funcPointerint number);
class Test
{
private:
int a;
public:
Test
{
a=5;
}
~Test
{
}
int calc(int number)
{
return a+number;
}
};
int FailureFunc(funcPointer classFunction, int number)
{
classFunction(number);
}
int main(void)
{
Test tst;
cout << FailureFunc(tst.calc, 10) << endl;
return 0;
}
Компилятор ругается: cannot convert parameter 1form int(int) to funcPointer
Может, я чего здесь намудрил?
P.S. А указатели на функции используются довольно часто. Как обработчики событий. Например, в glut, откуда данный пример и вытек. Я не могу передать в glutDisplayFunc(...) указатель на метод некоторого класса.
Вот, мучаюсь. Может, поможет кто.
P.P.S. Да, насчёт mem_func - это, всё-таки, немного не то. Просто функционалы обычно используются в контейнерах. mem_func(... в частности, для вызовов методов класса с параметрами внутри for_each(...). Я не могу понять, как их можно использовать вне их. Никогда сталкиваться не приходилось - опыта нет А в мертвечинке все примеры основаны на использовании for_each(...)
В общем, кто может помочь - буду благодарен.
P.P.S. Сорри, что класс не выровнен - я форматировал, не могу понять, почему собщение опять воровнялось по левому классу.
P.P.P.S. Да, со статическими методами - это я был, конечно, неправ.
Если можно изменять FailureFunc, то разумнее передать туда указатель на объект - то есть функция будет вызывать метод calc(int) любого объекта.
Если хочется вызывать произвольный метод произвольного объекта, можно вдобавок передать еще указатель на метод. Или, более кошерное решение через mem_fun:
#include <iostream>C glutDisplayFunc сложнее, там передается всего лишь указатель на функцию.
#include <functional>
using namespace std;
class A {
int a_;
public:
A : a_(5) {}
A(int a) : a_(a) {}
int calc(int n) {
return a_+n;
}
};
template <class T>
void f(T t, int number)
{
cout << t(number) << endl;
}
int main
{
A a(13);
f(bind1st(mem_fun(&A::calc&a 1);
return 0;
}
Значит, авторы api считают, что ей не должна требоваться дополнительная информация. Может у тебя проблемы в дизайне ?
Максимум, что приходит в голову - передавать данные (указатель на объект, например) через глобальную переменную.
Да, сейчас почитал на РСДН. Действительно, придётся редизайнить. Просто не хотелось делать глобальных переменных, классов. Но придётся. Просто это неправильно. Можно ещё сделать, в частности, так: написать несколько функций в main.cpp, которые скармливать glutDisplayFunc и иже с ней. А уже в них вызывать необходимые методы. Эх, а так хотелось ООП...
Можно ещё сделать, в частности, так: написать несколько функций в main.cpp, которые скармливать glutDisplayFunc и иже с ней. А уже в них вызывать необходимые методы. Эх, а так хотелось ООП...Сделай их статическими методами какого-нибудь класса
Да нет, я пошёл по описанному мной пути. Криво, но ладно...
Глют процедурно-ориентированный. А ты попытался его явно включить в ОО модель. Так не бывает.
Есть, например, GLOW - объектно-ориентированная обертка над Глютом.
Ввиду отсутствия МСДНаВ FAQ есть поиск по MSDN.
Я верю =) Но он-то сам пишет "обёртку над глютом", по ходу.
Так здесь ситуация из разряда "а нафига", а не из разряда "ну ты и смешной".
Если хочется вызывать произвольный метод произвольного объекта, можно вдобавок передать еще указатель на методДа говорю же, нет в С++ делегатов. Можно будет вызвать
произвольный метод произвольного объекта
_конкретного_ класса.
Максимум, что приходит в голову - передавать данные (указатель на объект, например) через глобальную переменнуюЕсть еще такое папское слово "thunk"! Санка - это коротенькая функция, из 2-3-х ассемблерных инструкций, создаваемая во время выполнения программы. например:
Ну и кажись для __fastcall конвенции будет работать Для __stdcall надо mov edx заменить на push, а для __ccall надо еще и вместо jmp написать call, а потом ret. Ну, я мог где-то ошибиться, но в целом идея понятная и проверено, что она работает... Переносимость правда страдает, эта CreateThunk должна быть разная для разных архитектур.
typedef int (* func_with_context_tint, void *);
typedef int (* func_tint);
CreateThunk(void * context, func_with_context_t f)
{
void * p = malloc(10);
//дальше ботаешь opcode-ы x86-assembler-а и по адресу p фигачишь такие комманды:
// mov edx, <константа, равная context>
// jmp <константа, равная f>
//потом
returnfunc_t) p);
}
PS 2 mef. Код выравнивают с помощью [_code_] ... [_\_code_] (только без "_").
Да говорю же, нет в С++ делегатов. Можно будет вызватьА я и не говорил про произвольный класс.
произвольный метод произвольного объекта
_конкретного_ класса.
И вообще, шаблоны рулят.
Есть еще такое папское слово "thunk"! Санка - это коротенькая функция, из 2-3-х ассемблерных инструкций, создаваемая во время выполнения программы.Ну это только если совсем припрет.
статья , в которой показано как сделать делегаты.
К слову, там не используется ни mem_fun, ни bind1st, ни генерация кода в рантайме, зато активно юзаются шаблоны.
Насчет делегатов на C++. Есть такая К слову, там не используется ни mem_fun, ни bind1st, ни генерация кода в рантайме, зато активно юзаются шаблоны.
type
PFunc = function (...):integer of object;
procedure F(PF: PFunc);
begin
end;
var SomeObj : TObj;
begin
F(SomeObj.Method);
end.
В этом случае в качестве параметра можно передавать только методы (а зачем компилятору знать, какому классу они принадлежат?). Обычные функции не пройдут.
Думал, что в плюсах можно что-нибудь подобное.. но что-то с первого взгляда не нахожу.
Оставить комментарий
VGordeev
Подскажите, плз, кто знает, с чем может быть связано:Есть функция, которая принимает указатель на функцию. Она описана в некотором .cpp файле.
Далее, есть класс, в котором есть некоторые методы. Создаю объект. Пытаюсь передать метод в качестве входного аргумента (пишу объект->метод а компилятор ругается, говорит, что это не указатель. В то же время, если в этом cpp создать функцию и передать в качестве параметра первой функции, то всё ок.
Надеюсь на ваши знания и понимание (не ржать!)