Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Коварство препроцессора ANSI C++ (оператор ##)


Автор: Абабо 5.2.2008, 23:10
У меня к вам немного странный вопрос.  Он возник у меня при попытке упрощения написания классов под Symbian. Суть вопроса следующая. Мне нужно написать макрос, заменяющий стандартные для Symbian-классов функции.  Вот одна из них:
Код

static Class *NewLC(Формальные параметры) 
{
Class* self = new (ELeave) Class;
    CleanupStack::PushL(self);
    self->ConstructClassL(Фактические параметры);
    return self; 
}

При написании макроса возникает проблема с передачей параметров – макрос должен получать два списка параметров (переменной длины) – формальные и фактические (например, (int a, float b) и (a,b)) и вставлять их в соотв. местах. Если бы я мог средствами препроцессора получить из формальных параметров фактические (например, (int a, float b) -> (a,b)), то проблемы бы не стояло – я бы передавал макросу переменный список формальных параметров (используя оператор …). Однако, как мне кажется, это за гранями возможности препроцессора, т.е. мне придётся вручную передавать фактические параметры. Но как тогда быть с отделением формальных параметров от фактических? Если заключать каждый список в скобки, то можно решить поставленную задачу. Так она решалась при использовании компилятора VC++:
Код

#define COMMON_NEWLC(Class, FormalArgs, ActualArgs) \    
    public: static Class *NewLC ## FormalArgs { \
        Class* self = CREATE(Class); \
        CleanupStack::PushL(self); \
        self->Construct ## Class ## L ## ActualArgs; \
        return self; }
class MyClass
{
    COMMON_NEWLC(MyClass, (int a, float b), (a,b))
…
};

Однако в моей задаче целевым компилятором является GCC, который пресекает подобную практику сообщениями ошибки: “ pasting "NewLC" and "(" does not give a valid preprocessing token”. 

Жду вашего совета. Спасибо.

Автор: SABROG 5.2.2008, 23:52
Сорри за оффтопик, а кто-то считает C++ кроссплатформенным... MSVC, GCC, BCC - у каждого свой стандарт, точно также как у MSSQL, MySQL, PosgreSQL, Oracle, SQLITE, FireBird...  smile 

Автор: JackYF 5.2.2008, 23:59
Цитата(Абабо @  5.2.2008,  22:10 Найти цитируемый пост)
public: static Class *NewLC ## FormalArgs { \

попробуй заменить на
Код

public: static Class *NewLC (## FormalArgs) { \

Автор: MAKCim 6.2.2008, 00:07
JackYF, 
и что?
в данном контексте скобки использовались для группировки множества параметров
в GCC такое недопустимо

Добавлено через 1 минуту и 15 секунд
твой способ
не позволит отличить формальные и фактические параметры

Автор: JackYF 6.2.2008, 01:03
Цитата(MAKCim @  5.2.2008,  23:07 Найти цитируемый пост)
в данном контексте скобки использовались для группировки множества параметров
в GCC такое недопустимо

значит, не туда глянул :(

Цитата(SABROG @  5.2.2008,  22:52 Найти цитируемый пост)
а кто-то считает C++ кроссплатформенным...

Я. Вышеприведенная ситуация - досадна, но я считаю, что система Symbian для разработчика [censored33! Пожалуйста, соблюдайте элементарные правила приличия при общении на форуме], если она заставляет его писать макросы, а не классы-наследники или ещё какую штатную фиговину.

Автор: Mayk 6.2.2008, 09:36
я одного не понял --- а смысл так делать?
Цитата(Абабо @  6.2.2008,  03:10 Найти цитируемый пост)
#define COMMON_NEWLC(Class, FormalArgs, ActualArgs) \    
    public: static Class *NewLC ## FormalArgs { \
        Class* self = CREATE(Class); \
        CleanupStack::PushL(self); \
        self->Construct ## Class ## L ## ActualArgs; \
        return self; }

Код

//a.cpp. пара ## выкинута нафик
#define COMMON_NEWLC(Class, FormalArgs, ActualArgs) \    
    public: static Class *NewLC  FormalArgs { \
        Class* self = CREATE(Class); \
        CleanupStack::PushL(self); \
        self->Construct ## Class ## L  ActualArgs; \
        return self; }
class MyClass
{
    COMMON_NEWLC(MyClass, (int a, float b), (a,b))
:
};



Цитата(cpp a.cpp)

    public: static MyClass *NewLC (int a, float b) { MyClass* self = CREATE(MyClass); CleanupStack::PushL(self); self->ConstructMyClassL (a,b); return self; }


Добавлено @ 09:38
Цитата(SABROG @  6.2.2008,  03:52 Найти цитируемый пост)
Сорри за оффтопик, а кто-то считает C++ кроссплатформенным...

расскажи об этом trolltech'ам. а то ребята бедные не в теме.

Автор: Абабо 6.2.2008, 10:38
Ну конечно же! Скобки же не должны обязательно прилегать к имени функции - во истину, всё простое гениально... А то я уже стал изощраться с конструкциями типа COMMON_NEWLC(MyClass,SEP(int a, float b),SEP(a,b)), потом же оказалось что мой VC++ 2003 (который я использую для отладки на эмуляторе) не поддерживает макросы с переменным числом параметров... 

Спасибо всем, а Mayk - особенно!

Автор: SABROG 6.2.2008, 11:24
Цитата(Mayk @ 6.2.2008,  09:36)
расскажи об этом trolltech'ам. а то ребята бедные не в теме.

Я не о том, что невозможно сделать кроссплатформенный проект. А о разности исполнения стандарта. Посмотри хотябы на спецификацию шаблонов, MSVC позволяют ее производить внутри класса, а GCC нет. В итоге C++ код, который должен быть кроссплатформенным вынужден использовать препроцессорные средства, чтобы проверить под каким из компиляторов производится сборка. А из-за разности алгоритма оптимизации создаются объектные файлы, которые могут не собираться с ошибками о всяких unresolved external, а на MSVC все ок. Еще есть косяки с пропиской области видимости объекта, там где MSVC схватает - GCC подавится. Еще в GCC есть тип long long , разные версии msvc его понимают по-разному. Или вот препроцессорная команда #pragma вообще для каждого компилятора разная, а MSVCшники упорно вставляют туда какое-нибудь подключение .lib файла и считают это нормальным. На самом деле подводных камней очень много.

Поэтому, если даже библиотека Qt сама кроссплатформенная, то вот приложения написанные с использованием Qt кроссплатформенными могут и не быть, даже если не используют системные API или сторонние библиотеки, которые были написанды для винды. Достаточно писать исходник только под msvc. Т.ч. для программиста желающиего писать кроссплатформенный софт правильно было бы выбрать mingw, если он пишет под виндой.

Автор: JackYF 6.2.2008, 11:49
Цитата(SABROG @  6.2.2008,  10:24 Найти цитируемый пост)
А о разности исполнения стандарта.

Ну да, она есть. Была и будет. И дальше что? smile

Автор: Mayk 6.2.2008, 12:33
Цитата(SABROG @  6.2.2008,  15:24 Найти цитируемый пост)
. А из-за разности алгоритма оптимизации создаются объектные файлы, которые могут не собираться с ошибками о всяких unresolved external, а на MSVC все ок.

С каких пор оптимизатор влияет на mangling?  smile 

Цитата(SABROG @  6.2.2008,  15:24 Найти цитируемый пост)
Еще в GCC есть тип long long , разные версии msvc его понимают по-разному. 

С какой версии MSVC понимает long long как тип, а не как синатксическую ошибку? smile 

Цитата(SABROG @  6.2.2008,  15:24 Найти цитируемый пост)
итоге C++ код, который должен быть кроссплатформенным вынужден использовать препроцессорные средства, чтобы проверить под каким из компиляторов производится сборка.

Всегда?

Цитата(SABROG @  6.2.2008,  15:24 Найти цитируемый пост)
Еще есть косяки с пропиской области видимости объекта, там где MSVC схватает - GCC подавится. 

Что такое прописка области видимости?  smile 

Цитата(SABROG @  6.2.2008,  15:24 Найти цитируемый пост)

Поэтому, если даже библиотека Qt сама кроссплатформенная, то вот приложения написанные с использованием Qt кроссплатформенными могут и не быть, даже если не используют системные API или сторонние библиотеки, которые были написанды для винды

Могут и не быть. А могут и быть. So what?

Цитата(SABROG @  6.2.2008,  15:24 Найти цитируемый пост)
Достаточно писать исходник только под msvc. Т.ч. для программиста желающиего писать кроссплатформенный софт правильно было бы выбрать mingw, если он пишет под виндой.

Этот фрагмент я не понял. 

Автор: SABROG 6.2.2008, 13:37
Если чесно, не хочеться углубляться в тему переносимости, тем более что это оффтопик.
Просто недавно пытался добавить возможность сборки одного проекта с помощью mingw, так он собирается msvc. Пришлось исправлять исходники в некоторых местах, столкнулся как-раз с теми проблемами, что описал выше. Unresolved External побороть мне так и не удалось, т.к. для этого надо проследить всю цепочку наследования класса, для меня это оказалось слишком сложно, т.к. вся цепочка достаточно большАя часть проекта, а сэмулировать проблему на мелкой программе не удалось. Mangling даже не при чем, т.к. в проекте не линковались библиотеки собранные на других компиляторах. Просто тесты показали, что в зависимости от уровня оптимизации методы и данные могут либо экспортироваться, либо не экспортироваться, т.к. если переменная или метод не используется они "киляются"...

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

Автор: tsrtg 12.2.2008, 01:41
Цитата

С какой версии MSVC понимает long long как тип, а не как синатксическую ошибку?


Все три установленные у меня версии MSVC (7.1, 8.0 и 9.0) понимают long long именно как 64-битный целочисленный тип.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)