IAR EC++ - как правильно?

Dec 09, 2003 7 Replies

Hi All,



Хочется использовать указатель на функцию - член класса:



class NETWORK { public: void SetGain(u8); void SetPhase(u8); void SetParameter(void (NETWORK::*Set)(u8), u8); };


void NETWORK::SetParameter(void (NETWORK::*Set)(u8), u8 param) { DoSomething();



(this->*Set)(param);



DoSomethingElse(); }



void main(void) { NETWORK network;



network.SetParameter(SetGain, 0); network.SetParameter(SetPhase, 1); }



Проблема в том, что IAR статически (!) расходует RAM (NEAR_Z) на каждый подобный вызов. Можно ли это обойти?


VLV


Tue, 09 Dec 2003 18:00:47 +0300 Vladimir Vassilevsky wrote to All:

VV> Хочется использовать указатель на функцию - член класса:

Такое желание должно быть оправдано - указатели на члены как правило более "толстые" (оверхедные) и работать с ними менее удобно, чем с простыми указателями.

VV> class NETWORK { VV> public: VV> void SetGain(u8); VV> void SetPhase(u8); VV> void SetParameter(void (NETWORK::*Set)(u8), u8); VV> };

VV> void NETWORK::SetParameter(void (NETWORK::*Set)(u8), u8 param) VV> { VV> DoSomething();

VV> (this->*Set)(param);

Зачем так сложно писать? SetParameter ведь является членом класса, поэтому писать this-> совсем необязательно.

VV> DoSomethingElse(); VV> }

VV> void main(void) VV> { VV> NETWORK network;

VV> network.SetParameter(SetGain, 0); VV> network.SetParameter(SetPhase, 1);

Хм, разве SetGain и SetPhase видны в этой области видимости? По-моему такой код не должен компилироваться. Могу спутать синтаксис, но должно выглядеть примерно так:

network.SetParameter(&network.SetGain, 0);

Т.е. нужно передавать полную информацию - и об объекте, и о смещении.

VV> }

VV> Проблема в том, что IAR статически (!) расходует RAM (NEAR_Z) на каждый VV> подобный вызов. Можно ли это обойти?

Не очень понятно, что там получается (вот если б на листинг со сгенеренным кодом посмотреть), но, возможно, тут имеет смысл использовать какое-то другое решение? Повторю - указатели на члены далеко не всегда лучшее решение, и часто проще и эффективнее обойтись без них. В данном случае, например, поскольку между первым и вторым параметрами функции SetParameter какой-то корреляции не просматривается, и в точке вызова функция, вызываемая по указателю, указана явно, то проще и правильнее вызвать функцию SetGain и потом SetParameter с одним аргументом... Конечно, если условия задачи другие, то и решение должно быть другим, но для того, чтобы что-то предложить, нужно быть в курсе условий задачи.

Tue Dec 09 2003 20:53, Harry Zhurov wrote to Vladimir Vassilevsky:

VV>> Хочется использовать указатель на функцию - член класса:

HZ> Tакое желание должно быть оправдано - указатели на члены как правило HZ> более "толстые" (оверхедные) и работать с ними менее удобно, чем с HZ> простыми указателями.

Ситуация: есть небольшие похожие функции-члены, но перед их вызовом и после вызова нужно сделать одинаковую вспомогательную работу. Кроме того, cаму функцию-член надо вызывать в цикле несколько раз. Хочется завернуть все это хозяйство в одну универсальную функцию, которой передается указатель на член. Конечно, вместо этого можно просто сделать switch из явных вызовов посередине универсальной функции и передавать параметр для свича, однако так делать некузяво.

VV>> (this->*Set)(param);

HZ> Зачем так сложно писать? SetParameter ведь является членом класса, HZ> поэтому писать this-> совсем необязательно.

Обязательно. Если ты вызываешь нестатическую функцию-член класса через указатель, то нужно передать указатель на класс.

VV>> network.SetParameter(SetGain, 0); VV>> network.SetParameter(SetPhase, 1);

HZ> Хм, разве SetGain и SetPhase видны в этой области видимости?

Конечно.

VV>> Проблема в том, что IAR статически (!) расходует RAM (NEAR_Z) на каждый VV>> подобный вызов. Можно ли это обойти? HZ> Hе очень понятно, что там получается (вот если б на листинг со HZ> сгенеренным кодом посмотреть),

Посмотрел. В RAM появляется статическая таблица указателей на вызываемые функции-члены класса. Зачем - непонятно. Ведь это константные адреса.

VLV

Dear Vladimir,

09 Dec 03 21:39, Vladimir Vassilevsky wrote to Harry Zhurov:

VV> Ситуация: есть небольшие похожие функции-члены, но перед их вызовом и VV> после вызова нужно сделать одинаковую вспомогательную работу. Кроме того, VV> cаму функцию-член надо вызывать в цикле несколько раз. Хочется завернуть VV> все это хозяйство в одну универсальную функцию, которой передается VV> указатель на член. Конечно, вместо этого можно просто сделать switch из VV> явных вызовов посередине универсальной функции и передавать параметр для VV> свича, однако так делать некузяво.

А можно отдельно описать ф-ции DoSomething() и DoSomethingElse(). Далее вызывать твои ф-ции SetSomething() напрямую, из которых вызывать эти стандартные прологи и эпилоги. Ф-цию SetParameter() удалить как лишнюю и вредную.

Sincerely yours, Old Greaser.

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 располагается в ОЗУ, с этим ничего не поделаешь - архитектура такая. Вот если бы у него хватало ума размещать эту таблицу во флеш, то это было бы, наверное, то, что тебе надо. Но пока он не такой "умный" (что в перспективе может составить предмет беседы с иаровцами). Хотя не уверен, можно ли это в принципе сделать - не помню, разрешается ли адресная арифметика с указателями на члены совместно с обычными указателями. Если не разрешается, то тогда принципиальных проблем разместить в флеш нет, а если разрешается, то тады "ой", жить ему в ОЗУ.

Tue Dec 09 2003 23:12, Serge Bryxin wrote to Vladimir Vassilevsky:

VV>> Ситуация: есть небольшие похожие функции-члены, но перед их вызовом и VV>> после вызова нужно сделать одинаковую вспомогательную работу. Кроме VV>> того, cаму функцию-член надо вызывать в цикле несколько раз. Хочется VV>> завернуть все это хозяйство в одну универсальную функцию, которой VV>> передается указатель на член. Конечно, вместо этого можно просто VV>> сделать switch из явных вызовов посередине универсальной функции и VV>> передавать параметр для свича, однако так делать некузяво.

SB> А можно отдельно описать ф-ции DoSomething() и DoSomethingElse(). Далее SB> вызывать твои ф-ции SetSomething() напрямую, из которых вызывать эти SB> стандартные прологи и эпилоги. Ф-цию SetParameter() удалить как лишнюю и SB> вредную.

Функций SetSomething() несколько десятков. Они могут вызываться как сами по себе, так и в составе примерно такой процедуры:

DoSomething(); for(ci = 0; ci < number; ci++) SetSomething(ci); DoSomethingElse();

Hачало и конец можно сделать в виде враппера, как предложил Harry Zurov, но с циклом так не расправишься.

SB> Sincerely yours, SB> Old Greaser.

VLV

Wed, 10 Dec 2003 22:35:01 +0300 Vladimir Vassilevsky wrote to Serge Bryxin:

[...]

VV> DoSomething(); VV> for(ci = 0; ci < number; ci++) SetSomething(ci); VV> DoSomethingElse();

VV> Hачало и конец можно сделать в виде враппера, как предложил Harry Zurov, VV> но с циклом так не расправишься.

// -------------------------------------------- class TSlon { public: TSlon() { DoSomething(); } ~TSlon() { DoSomethingElse(); } };

void f() { ... //

{ TSlon sl; for(ci = 0; ci < number; ci++) SetSomething(ci); }

... // }

// --------------------------------------------

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

Dear Vladimir,

10 Dec 03 22:35, Vladimir Vassilevsky wrote to Serge Bryxin:

SB>> можно отдельно описать ф-ции DoSomething() и DoSomethingElse(). SB>> Далее вызывать твои ф-ции SetSomething() напрямую, из которых SB>> вызывать эти стандартные прологи и эпилоги.

VV> Функций SetSomething() несколько десятков. Они могут вызываться как VV> сами по себе, так и в составе примерно такой процедуры:

VV> DoSomething(); VV> for(ci = 0; ci < number; ci++) SetSomething(ci); VV> DoSomethingElse();

VV> Hачало и конец можно сделать в виде враппера, как предложил Harry Zurov, VV> но с циклом так не расправишься.

Так и я, в общем-то, враппер предлагаю. А почему с циклом не расправиться? Hапример:

SetSomething(int start, int number, bool init) { register int i; if( init ) DoSomething(); for( i=start; i<start+number; i++ ) { ActuallySet(i); } if( init ) DoSomethingElse(); }

Hа самом деле по-хорошему ф-цию ActuallySet() описывать не надо. В цикле - собственно алгоритм установки ( i.e. тело твоей текущей SetSomething() ). Ибо каждый лишний CALL в циклах - плохо в принципе. Если скорость вызова ActuallySet() без пролога и эпилога смертельно критична, можно таки ее описать (а параметр init опустить, возожно и параметр start опустить, если цикл всегда от 0), но в общем случае я бы не стал.

До кучи: #define SetSomething1(X) SetSomething((X),1,false)

Блин, насколько все это проще и изящнее на Асме! :-) Флаг T взвел, CALL прямо на метку ActuallySet, BRTC дальше, иначе RET.

Sincerely yours, Old Greaser.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required