В JavaScript'е, например, для этого есть функция eval, которая пытается выполнить текстовую строку. Единственное решение, которое я нашла для C++: создавать некий массив, а лучше map с отображением символьного имени функции на указатель на нее.
void updateInfo()
{
...
}
map <string, void (*)()> mFunctions;
mFunctions["updateInfo"] = updateInfo;
//и так далее для всех функций, что нужно использовать
...
string sFunctionName;
...
//где-то там считали имя функции
mFunctions[sFunctionName]();//выполнили нужную функцию
mFunctions["updateInfo"]();//то же самое
И для каждой вновь появившейся функции придется руками этот массив править и перекомпилировать код.
Updated 11.04.2007
[Сразу несколько человек в комментариях заметили, что есть способ лучше, предложенный Александреску в "Современном проектировании".]
Ну и ладно. Зато появился повод поговорить об указателях на функции.
Указатели на функцию - вещь интересная и полезная. Настолько полезная, что появился отдельный сайт, посвященный исключительно указателям на функции - www.function-pointer.org. Сейчас он редиректит вот сюда: The Function Pointer Tutorials. Также в C++ FAQ Lite есть раздел, посвященный указателям на функции. Я пробегусь по основным моментам, но все равно почитайте эти ссылки там много интересно и полезного.
Указатель на функцию может выглядеть например так:
//объявляется указатель под именем pt2Function
int (*pt2Function)(float, char, char);
Указатель на функцию-член класса может выглядеть так.
//объявляется указатель под именем pt2Member
int (TMyClass::*pt2Member)(float, char, char);
Указатель на константную функцию-член класса может выглядеть так.
//объявляется указатель под именем pt2ConstMember
int (TMyClass::*pt2ConstMember)(float, char, char) const;
(Все примеры из The Function Pointer Tutorials)И этот указатель на функцию можно всяко-разно использовать. Например, как я использовала в начале поста. Или его передать в функцию. И это получится уже коллбэк (callback).
void sort_ints(int* begin_items, int num_items,
int (*cmpfunc)(int, int) );
(пример из Wikipedia)Это была объявлена функция сортировки, которая в для операции сравнения использует переданную cmpfunc.
Если вам вдруг нужно в качестве коллбэка передать нестатическую функцию-член класса, то, возможно, вы хотите странного. Но сделать это можно, вот объяснение как это сделать из C++ FAQ Lite, вот объяснение из The Function Pointer Tutorials.
Есть еще такое интересное понятие как функтор (functor, сокращение от function object).
В C++ можно сделать так: объявить класс, в котором переопределен operator(). Работа с таким объектом выглядит как работа с функцией. Но при этом это полноценный объект, возможностей у него больше, чем у функции. Он, например, может хранить состояние. В нем можно опеределить данные, другие функции и с ними работать.
template <class Fun>
void foo(Fun f)
{
//...
f();
//...
}
Обычно под функтором понимается любой объект, с которым можно работать как с функцией, то есть в C++ функтором являются и объекты, с переопределенным operator(), и собственно функции. Но тут я встречала разночтения. Иногда, когда говорят про функторы, обычные функции туда не включают (этот подход мне больше по душе). А иногда еще встречается термин функционоид (functionoid), для функторов, не являющихся обычными функциями.
Сочетание функторов с темплейтами приводит к очень интересным возможностям. Обратите внимание, в приведенном выше примере f может являться как функтором, так и указателем на функцию.
Ссылки:
comp.lang.c++.moderated Newbie question about operator() and functors in general
Функторы очень интенсивно используются в STL, о чем рассказывается здесь: Function Objects.
Очень хорошо, со всякими ссылками о функторах написано в Function object, Wikipedia
Указатели на функции-члены и реализация самых быстрых делегатов на С++
Указатели на функции члены класса
В С++, есть ещё тип такой - сигнатура. Помню с ним столкнулся, когда писал генератор парсеров для вызова функции по имени.
ОтветитьУдалитьПричём задача была автоматический распознать количество параметров и их типы у функций. Вот тут пришлось извлекать сигнатуры и парсить их во время компиляции.
А вообще, конечно, проблема С++ в том, что функции в этом языке не являются объектами первого класса.
STL и основная часть boost`а пытаются компенсировать этот недостаток, добавить в С++ некоторые возможности функциональных языков.
Это, конечно, значительно увеличивает мощь языка. Но всё равно некоторые задачи решаются не так просто как бы хотелось.
Если бы С++ реализовывал внутренне хотя бы такие вещи как lambda-functions, currying, higher-order functions, то этому языку не было бы равных в прикладной области.
Помню, как мы с приятелем в годы студенчества долго и тщетно пытались объявить массив функций, тогда это у нас не очень получилось. И лишь сейчас, много лет спустя, я, наконец, узнал как это делается. Спасибо!
ОтветитьУдалитьЗасыпался я как-то вопросом: Что будет, если указателю на функцию-член присвоить указатель на виртуальный метод предка, а потом вызвать её на экземпляре потомка?
ОтветитьУдалитьhttp://singalen.livejournal.com/49410.html#cutid1
Сделал вывод: указатель на функцию-член хранит И индекс в VMT, И указатель на функцию. Ну и флаг, указывающий, что именно хранится.
Хороший обзор, спасибо.
ОтветитьУдалитьТолько линк на функторы от sgi поправьте :)
В JS не нужно тут применять eval, там есть тип - функция (точнее - класс), даём значение переменной типа функция и вызываем.
ОтветитьУдалитьАлександреску про это хорошо пишет - у него есть глава про "функторы"
ОтветитьУдалитьЕщё могу добавить интересные ссылки -
ОтветитьУдалитьhttp://www.gamedev.ru/faq/?id=34
http://dobrokot.nm.ru/cpp/CppMethodsCallback.html
functor, сокращение от function object
ОтветитьУдалитьНе очень похоже на сокращение. Насколько я знаю, это термин из математики и к словосочетанию function object никакого отношения не имеет - просто Коплин, нимало не стесняясь, взял и позаимствовал его оттуда.
И еще мои 5 копеек в спор о том, входят ли в понятие функтор обыкновенные функции. На мои взгляд, однозначно нет: удобно иметь термин для обозначения классов с operator(). Когда я пишу программу на STL и сталкиваюсь с тем, что мне нужно передать алгоритму какое-либо действие, я говорю: «пора писать функтор» - в этом случае я не имею ввиду функцию, я имею ввиду класс.
Так же пишет и тот самый Коплин (которого, вслед за Эккелем – “TIC++”, vol.2, ch.10 Command, – считаю отцом данного понятия): «Функторы исполняют роль функций, но при этом создаются, передаются в параметрах и т. д. как объекты. Реализация функторов в С++ предельно проста: функтор оформляется в виде класса, содержащего всего одну функцию» (“Advanced C++ Programming Styles and Idioms”, 5.6).
> а лучше map с отображением символьного имени функции на указатель на нее.
ОтветитьУдалитьА ещё лучше не указатель, а функтор (самая удобная и распространённая конструкция: boost::shared_ptr<boost::function<...> >).
Хороший обзор, спасибо.
ОтветитьУдалитьВсем пожалуйста, старалась :-)
Только линк на функторы от sgi поправьте :)
Поправила, спасибо.
Не очень похоже на сокращение. Насколько я знаю, это термин из математики и к словосочетанию function object никакого отношения не имеет - просто Коплин, нимало не стесняясь, взял и позаимствовал его оттуда.
ОтветитьУдалитьЯ встречала разные мнения по этому поводу. В том числе и то, что это сокращение, и не надо его путать с математическим термином. На русский их еще иногда переводят как "фанкторы".
Для себя я таки приняла решение, что буду называть эти объекты функторами, и функции туда не включаю. Сокращение или нет - это дело десятое... Но когда читаю какую-либо литературу держу в голове, что разные люди понимают под этим разные вещи и к этому надо быть готовой.
в факе
ОтветитьУдалитьhttp://www.parashift.com/c++-faq-lite/pointers-to-members.html
есть вопрос : "Могу ли я привести указатель на функцию к void*" . Был дан категорический ответ нет.
Бывают моменты когда это надо (например передавать указатель на callback функцию которая есть функцией класса (даже если ни есть членом класса)).В общем поптыку присваивание одного указателя другому (которое ни возможно осуществить из-за невозможности приведения типа) стоит заменить на побайтное копироние значения указателя посредствам хотя бы той же memcpy ();
Если вам вдруг нужно в качестве коллбэка передать нестатическую функцию-член класса...
ОтветитьУдалить...то можно сделать
так.
Да Аноним правильно сказал, что у Александреску в "Современном проектировании" есть хороший пример реализации связи имени функции с указателем не нее через std::map. У него не надо это делать
ОтветитьУдалить"И для каждой вновь появившейся функции придется руками этот массив править и перекомпилировать код"
Одно из самых интересных применений функторов - что-то типа этого кода:
ОтветитьУдалитьdoit( Find( "string" ) );
фишка в том, что Find() - это одновременно и конструктор функтора (который запоминает "string"), и адрес оператора (т.е. функции) этого функтора, который вызывается внутри doit() как алгоритм для выполнения поиска.
Прекрасную иллюстрацию кода можно найти в STL, поискав там реализацию алгоритмов типа foreach (не забудьте преобразовать код в читабельный вид:).
Пример:
template(*class _T*)
class Find
{
const _T &_searched_item;
public:
inline Find( const _T &rhs )
: _searched_item( rhs )
{}
inline bool operator()( const _T &rhs ) const
{ return( rhs == _searched_item ); }
};
// ps. замените (* и *) на треугольные скобки.
про известное
ОтветитьУдалитьint*(*(*get_it())())[]
{
return 0;
}
забыли :)
Единственное решение, которое я нашла для C++: создавать некий массив, а лучше map с отображением символьного имени функции на указатель на нее.
ОтветитьУдалитьИ для каждой вновь появившейся функции придется руками этот массив править и перекомпилировать код.
[Сразу несколько человек в комментариях заметили, что есть способ лучше, предложенный Александреску в "Современном проектировании".]
Что-то я не понял насчёт лучше. Можно избежать надобности в перекомпиляции кода, если правильно код организовать, но это никак не отменит того, что нужно будет создавать ассоциативный массив и для каждой новой функции править его руками - выполнять добавление пары имя_функции-указатель_на_функцию (такие добавления необязательно сосредотачивать в одном месте).
2Adept:
ОтветитьУдалитьЧто-то я не понял насчёт лучше.
Я не смогу пояснить... У меня есть планы добраться-таки до Александреску и разобраться с тем, что он предлагает по этому поводу, но пока я не разбиралась.
Вот ещё интересная ссылка по указателям на функции. Советую почитать.
ОтветитьУдалитьhttp://rsdn.ru/article/cpp/fastdelegate.xml
Расскажите, кто конечно знает, подробнее про ссылки на функции. Например, ведь можно написать и так:
ОтветитьУдалитьtypedef T (& fREF)(T, T)
Вот здесь http://www.dslev.narod.ru/PointersToMembers.htm синтаксис и применение интересное. Посмотрите кто желает.
ОтветитьУдалитьПосле 15-минутного применения "метода математического тыка" выяснил как объявить массив указателей на функцию в куче
ОтветитьУдалитьvoid (**fn_ptr)(int,float) = new (void(*[100])(int,float));
Я тут под g++ немного шаманил с указателями на ф-цию и преобразованием указателей.
ОтветитьУдалить-------------------
int *yyy = 0;
void (*fpoint) () = NULL;
fpoint = reinterpret_cast(yyy);
fpoint = (void (*)())yyy;
-------------------
Написал такое. и компилер мотюкается на 3 ю строку (4я - сишное преобразование нормально компилится). в принципе оно и понятно нефиг указатель на инт к указателю на ф-цию преобразовывать. Вопрос в другом, почему компилятор пропускает вот это -
fpoint = (void (*)())yyy;
Ведь все равно (я де то тут читал) сишное преобразование типов в конечном итоге сведется к то му же reinterpret_cast.
А по моему и этот способ неплох. Это смотря где использовать.
ОтветитьУдалитьint*(*(*get_it())())[]
ОтветитьУдалить{
return 0;
}
void (**fn_ptr)(int,float) = new (void(*[100])(int,float));
Наркоманы штоле???