Tue, 09 Dec 2003 21:39:50 +0300 Vladimir Vassilevsky wrote to Harry Zhurov:
VV>>> Хочется использовать указатель на функцию - член класса:
HZ>> Tакое желание должно быть оправдано - указатели на члены как правило HZ>> более "толстые" (оверхедные) и работать с ними менее удобно, чем с HZ>> простыми указателями.
VV> Ситуация: есть небольшие похожие функции-члены, но перед их вызовом и VV> послевызова нужно сделать одинаковую вспомогательную работу. Кроме того, VV> cаму функцию-член надо вызывать в цикле несколько раз. Хочется VV> завернуть все это хозяйство в одну универсальную функцию, которой VV> передается указатель на член. VV> Конечно, вместо этого можно просто сделать switch из явных вызовов VV> посередине универсальной функции и передавать параметр для свича, VV> однако так делать некузяво.
Да, со свитчем некузяво.
Соображения такие. Всякое такое, что требует одинаковых действий до и действий после, бывает удобно завернуть в "обертку", т.е. написать класс-враппер, где в конструкторе выполняются действия ДО, а в деструкторе - действия ПОСЛЕ. При использовании нужно просто создать объект и работать с ним, а ДО и ПОСЛЕ выполнится автоматически, путем неявного вызова конструктора и деструктора. Например:
class TSlon { public: TSlon() { DoSomthing(); } void SetGain(u8); ...
~TSlon() { DoSomthingElse(); }
private: ... }
void main() { ... TSlon slon;
slon.SetGain(0); }
Еще один вариант - использовать виртуальные функции, там таблица указателей размешается во флеши (специально проверял). При этом нужно каждый параметр будет сделать отдельным классом, производным от одного базового. В этом случае, однако, появляются накладные расходы по памяти - каждый класс будет содержать два байта указателя vptr. Зато есть бОльшая гибкость - можно вызывать по одному указателю разные функции.
Ну, и остается традиционный вариант с помощью обычных указателей. Если не хочется выносить функции в глобальную область видимости, то можно их сделать статическими и работать как с обычными. Этот вариант, имхо, наиболее близок к твоей реализации. Тем более, что все равно приходится явно метать this.
В общем, тут тебе думать/выбирать.
[...]
VV>>> network.SetParameter(SetGain, 0); VV>>> network.SetParameter(SetPhase, 1);
HZ>> Хм, разве SetGain и SetPhase видны в этой области видимости?
VV> Конечно.
Да нет, их область видимости ограничена классом NETWORK, поэтому в таком виде компилятор выдает ошибку:
------------------------------------------------- network.SetParameter(SetGain, 0); ^ "D:\slon\IAR\AVR\VLV\slon.cpp",33 Error[Pe020]: identifier "SetGain" is undefined
-------------------------------------------------
Т.е. не видит он тут такого имени. Чтобы он увидел, нужно написать:
network.SetParameter(NETWORK::SetGain, 0);
VV>>> Проблема в том, что IAR статически (!) расходует RAM (NEAR_Z) на каждый VV>>> подобный вызов. Можно ли это обойти? HZ>> Hе очень понятно, что там получается (вот если б на листинг со HZ>> сгенеренным кодом посмотреть),
VV> Посмотрел. В RAM появляется статическая таблица указателей на вызываемые VV> функции-члены класса. Зачем - непонятно. Ведь это константные адреса.
Именно. Вот он и размещает эти адреса в сегменте const. Просто сегмент const в AVR располагается в ОЗУ, с этим ничего не поделаешь - архитектура такая. Вот если бы у него хватало ума размещать эту таблицу во флеш, то это было бы, наверное, то, что тебе надо. Но пока он не такой "умный" (что в перспективе может составить предмет беседы с иаровцами). Хотя не уверен, можно ли это в принципе сделать - не помню, разрешается ли адресная арифметика с указателями на члены совместно с обычными указателями. Если не разрешается, то тогда принципиальных проблем разместить в флеш нет, а если разрешается, то тады "ой", жить ему в ОЗУ.