И еще раз про #define

rosali

Сколько бы ни ругали C-препроцессор, он останется гениальным изобретением .
Вот код одной из одной библиотеки:


#define EMPTY
#define VAPI_ERROR_LIST \
VAPI_ERROR_INFO( VAPI_OK, = 0 ,"Operation Completed Successfully") \
VAPI_ERROR_INFO( VAPI_EGEN, =-255,"Generic error") \
VAPI_ERROR_INFO( VAPI_EFATAL, EMPTY,"Fatal error (Local Catastrophic Error)") \
VAPI_ERROR_INFO( VAPI_EAGAIN, EMPTY,"Resources temporary unavailable") \
VAPI_ERROR_INFO( VAPI_ENOMEM, EMPTY,"Not enough memory") \
VAPI_ERROR_INFO( VAPI_EBUSY, EMPTY,"Resource is busy") \
VAPI_ERROR_INFO( VAPI_ETIMEOUT, EMPTY,"Operation timedout") \
VAPI_ERROR_INFO( VAPI_EINTR, EMPTY,"Operation interrupted") \
VAPI_ERROR_INFO( VAPI_EPERM, EMPTY,"Not enough permissions to perform operation")\
VAPI_ERROR_INFO( VAPI_ENOSYS, EMPTY,"Not implemented") \
VAPI_ERROR_INFO( VAPI_ESYSCALL, EMPTY,"Error in an underlying O/S call") \
VAPI_ERROR_INFO( VAPI_EINVAL_PARAM, EMPTY,"Invalid Parameter") \
VAPI_ERROR_INFO( VAPI_EINVAL_HCA_HNDL, EMPTY,"Invalid HCA Handle.") \
VAPI_ERROR_INFO( VAPI_EINVAL_HCA_ID, EMPTY,"Invalid HCA identifier") \
VAPI_ERROR_INFO( VAPI_EINVAL_COUNTER, EMPTY,"Invalid key counter index") \
VAPI_ERROR_INFO( VAPI_EINVAL_COUNT_VAL, EMPTY,"Invalid counter value") \
VAPI_ERROR_INFO( VAPI_EINVAL_PD_HNDL, EMPTY,"Invalid Protection Domain") \
VAPI_ERROR_INFO( VAPI_EINVAL_RD_UNSUPPORTED, EMPTY,"RD is not supported") \
VAPI_ERROR_INFO( VAPI_EINVAL_RDD_HNDL, EMPTY,"Invalid Reliable Datagram Domain") \
VAPI_ERROR_INFO( VAPI_EINVAL_AV_HNDL, EMPTY,"Invalid Address Vector Handle") \
VAPI_ERROR_INFO( VAPI_E2BIG_WR_NUM, EMPTY,"Max. WR number exceeds capabilities") \
VAPI_ERROR_INFO( VAPI_E2BIG_SG_NUM, EMPTY,"Max. SG size exceeds capabilities") \
VAPI_ERROR_INFO( VAPI_EINVAL_SERVICE_TYPE, EMPTY,"Invalid Service Type") \
VAPI_ERROR_INFO( VAPI_ENOSYS_ATTR, EMPTY,"Unsupported attribute") \
VAPI_ERROR_INFO( VAPI_EINVAL_ATTR, EMPTY,"Can not change attribute") \
VAPI_ERROR_INFO( VAPI_ENOSYS_ATOMIC, EMPTY,"Atomic operations not supported") \
VAPI_ERROR_INFO( VAPI_EINVAL_PKEY_IX, EMPTY,"Pkey index out of range") \
VAPI_ERROR_INFO( VAPI_EINVAL_PKEY_TBL_ENTRY, EMPTY,"Pkey index point to invalid Pkey") \
VAPI_ERROR_INFO( VAPI_EINVAL_QP_HNDL, EMPTY,"Invalid QP Handle") \
VAPI_ERROR_INFO( VAPI_EINVAL_QP_STATE, EMPTY,"Invalid QP State") \
VAPI_ERROR_INFO( VAPI_EINVAL_SRQ_HNDL, EMPTY,"Invalid SRQ Handle") \
VAPI_ERROR_INFO( VAPI_ESRQ, EMPTY,"SRQ is in error state") \
VAPI_ERROR_INFO( VAPI_EINVAL_EEC_HNDL, EMPTY,"Invalid EE-Context Handle") \
VAPI_ERROR_INFO( VAPI_EINVAL_MIG_STATE, EMPTY,"Invalid Path Migration State") \
VAPI_ERROR_INFO( VAPI_EINVAL_MTU, EMPTY,"MTU violation") \
VAPI_ERROR_INFO( VAPI_EINVAL_PORT, EMPTY,"Invalid Port Number") \
VAPI_ERROR_INFO( VAPI_EINVAL_RNR_NAK_TIMER, EMPTY,"Invalid RNR NAK timer field") \
VAPI_ERROR_INFO( VAPI_EINVAL_LOCAL_ACK_TIMEOUT, EMPTY,"Invalid Local ACK timeout field") \
VAPI_ERROR_INFO( VAPI_E2BIG_RAW_DGRAM_NUM, EMPTY,"Number of raw datagrams QP exeeded") \
VAPI_ERROR_INFO( VAPI_EINVAL_QP_TYPE, EMPTY,"Invalid special QP type") \
VAPI_ERROR_INFO( VAPI_ENOSYS_RAW, EMPTY,"Raw datagram QP not supported") \
VAPI_ERROR_INFO( VAPI_EINVAL_CQ_HNDL, EMPTY,"Invalid Completion Queue Handle") \
VAPI_ERROR_INFO( VAPI_E2BIG_CQ_NUM, EMPTY,"Number of entries in CQ exceeds Cap.") \
VAPI_ERROR_INFO( VAPI_CQ_EMPTY, EMPTY,"CQ is empty") \
VAPI_ERROR_INFO( VAPI_EINVAL_VA, EMPTY,"Invalid Virtual Address") \
VAPI_ERROR_INFO( VAPI_EINVAL_LEN, EMPTY,"Invalid length") \
VAPI_ERROR_INFO( VAPI_EINVAL_ACL, EMPTY,"Invalid ACL") \
VAPI_ERROR_INFO( VAPI_EINVAL_PADDR, EMPTY,"Invalid physical address") \
VAPI_ERROR_INFO( VAPI_EINVAL_OFST, EMPTY,"Invalid offset") \
VAPI_ERROR_INFO( VAPI_EINVAL_MR_HNDL, EMPTY,"Invalid Memory Region Handle") \
VAPI_ERROR_INFO( VAPI_EINVAL_MW_HNDL, EMPTY,"Invalid Memory Window Handle") \
VAPI_ERROR_INFO( VAPI_EINVAL_OP, EMPTY,"Invalid operation") \
VAPI_ERROR_INFO( VAPI_EINVAL_NOTIF_TYPE, EMPTY,"Invalid completion notification type") \
VAPI_ERROR_INFO( VAPI_EINVAL_SG_FMT, EMPTY,"Invalid scatter/gather list format") \
VAPI_ERROR_INFO( VAPI_EINVAL_SG_NUM, EMPTY,"Invalid scatter/gather list length") \
VAPI_ERROR_INFO( VAPI_E2BIG_MCG_SIZE, EMPTY,"Number of QPs attached to multicast groups exceeded") \
VAPI_ERROR_INFO( VAPI_EINVAL_MCG_GID, EMPTY,"Invalid Multicast group GID") \
VAPI_ERROR_INFO( VAPI_EOL, EMPTY,"End Of List") \
VAPI_ERROR_INFO( VAPI_ERROR_MAX, EMPTY,"Dummy max error code : put all error codes before it") \

enum VAPI_ret_t {
#define VAPI_ERROR_INFO(A, B, C) A B,
VAPI_ERROR_LIST
#undef VAPI_ERROR_INFO
};


Кто-то уже догадался, как воспользоваться таким странным определением enum-а?

6yrop

C-препроцессор, он останется гениальным изобретением

как раз сейчас переписываю модуль с C# на C++. Вот два класса описаны в двух файлах.
Файл A.h

#pragma once
#include "B.h"
class A
{
B b;
};


Файл B.h

#pragma once
#include "A.h"
class B
{
void f(A a);
};


Компилятор С++ не может разрешить такие циклические #include-ы. Причем по описанию ошибки и месту, на которое он указывает, нельзя догадаться, что не так.
Реальная ситуация была немного сложнее, циклическая ссылка была через промежуточные классы, много времени потратил пока разобрался.
У С# таких проблем не возникает, и это естественно - зачем мне следить за порядком описания классов.

rosali

Ну, привет, а при чем тут С-препроцессор?... Я же про #define а не про #include. То что ты хочешь и в одном файле не просто написать, кажиcь достаточно


class A;
class B
{
void f(A a);
};
class A
{
B b;
};


Но, это, в новой студии (VS7) работает, а в старой (VS6) не работало. Там нельзя было сказать такого вообще никак. Я на это наткнулся вот в каком случае:


class A
{
B f;
};
class B
{
A g;
}


... Так или иначе, тут все проблемы в однопроходности (изначально задуманной) С-компилятора. Сейчас на эту однопроходность всем давно плевать, а в языке все эти forward declarations остались... Убого, конечно, но я же не о том
Я о том, что в языке должна быть полноценная возможность оперировать с самим кодом.
#define A(x) ...x...
это конечно не предел мечтаний, но в Яве например и того нету. Из-за этого так популярны генераторы Явового кода. А как потом синхрониз[ ировать|овывать|овать ] (а как правильно то? ) изменения сделанные в сгенерированном и генерирующем, вообще мне плохо понятно...

6yrop

ну да, я немного не в тему , просто фанаты С++ пытались это оспаривать...

maggi14

Стильно. А нахрена это?

Dasar

> А как потом синхронизировать изменения сделанные в сгенерированном и генерирующем, вообще мне плохо понятно
Просто не надо руками менять тот код, который генерируется автоматически, и все будет в порядке.

rosali

А нахрена это?

А вот я взял потом и с полпинка написал


static const char * CodeMessage(VAPI_ret_t ret)
{
switch(ret)
{
#define VAPI_ERROR_INFO(A, B, C) \
case A: return( C );
VAPI_ERROR_LIST
#undef VAPI_ERROR_INFO
default: return( "Unknown error code" );
}
}


Красиво да?
Это своего рода рефлексия -- программа воспринимает себя саму как данные...
Нет, я не предлагаю все enum-ы или struct-ы так писать, но что-то в этом есть... Надо вообще в язык встраивать возможность работать с метаданными, чтобы можно было, скажем, написать цикл по всем полям структуры?..

rosali

Просто не надо руками менять

Ну это все понятно, но программисты разные бывают.. Одни воспринимают твое "не надо" как табу, а другим кажется, что в особых случаях все таки можно

maggi14

Да, реально клево. Надо подумать, может, имеет смысл свойй код оформлять похожим образом

Ivan8209

А ещё есть хорошее предложение использовать приличный язык
программирования, а не ставить костыли и подпорки, вылезающие в самое
неподходящее время.
---
...Я работаю антинаучным аферистом...

maggi14

А чем С/С++ неприличный?

sergey_m

Вот я давеча объебался с #define. Переменная была определена сначала в enum {}, а потом в #define. И компилятор не выругался, что типа redefined.

Ivan8209

Много чем.
Например, отсутствием операции масштабирования, из-за чего сдвиг
Бернулли производится через задницу.
Ещё отсутствием операции возведения даже в целочисленную степень.
Отсутствием приличных, постоянных констант.
Отсутствием чёткого соглашения о знаке остатка от деления.
Я могу много чего рассказать про Си.
---
...Я работаю...

Marinavo_0507

> Отсутствием приличных, постоянных констант.
Они временные что-ли?

smnikiforov

>Ещё отсутствием операции возведения даже в целочисленную степень.
лол тебя научить её писать?
>Я могу много чего рассказать про Си.
это безусловно минус сей, но его можно стерпеть

Ivan8209

const int N=10;
long a[N];
---
...Я работаю антинаучным аферистом...

Ivan8209

unsigned long a[2**N];
---
...Я работаю антинаучным аферистом...

Marinavo_0507

работает
гнутое расширение afaik

Marinavo_0507

Real Progammer can write FORTRAN in any language.

Ivan8209

REAL*8 Programmer?
---
...Я работаю антинаучным аферистом...

Ivan8209

В своё время это не работало.
Из-за чего для Си понадобились костыли наподобие его препроцессора.
---
...Я работаю антинаучным аферистом...

rosali

сначала в enum {}, а потом в #define

Как говорится, язык программирования должен позволять программисту застрелиться
И компилятор не выругался, что типа redefined

А это он вообще никак не мог бы допетрить. Препроцессор не понимает слова enum, а компилятору потом уже и нечего было понимать...

Ivan8209

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

rosali

Из-за чего для Си понадобился ... препроцессор

Так из-за чего, все-таки он понадобился? Из-за
long a[2**N];

Или я чего недопонял?

Ivan8209

Посмотри, какие задачи решаются с помощью текстовых подстановок, и
поймёшь, чего не хватает в Си.
Это, например: объявление констант, типов, интерфейсов,
встраиваемые подпрограммы,--- в общем, куча всего того, что в
приличных языках делается сразу, изначально присущим.
---
...Я работаю антинаучным аферистом...

maggi14

>Я могу много чего рассказать про Си.
это безусловно минус сей, но его можно стерпеть

maggi14

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

yolki

Это после f2c-то?
имхо проще объектники линковать

rosali

объявление констант, типов, интерфейсов,
встраиваемые подпрограммы

Единственное отличие #define от const int, то что в некоторых реализациях второй не может быть размером массива. Массивы в С используются крайне редко (это же не фортран! ) так что этим можно пренебречь. А в остальном, есть слова typedef, inline, при чем тут препроцессор?...
Препроцессор по существу нужен для манипуляций кодом как данными. Создатели С побоялись, что на некоторых программах компилятор будет зависать и сделали препроцессор не алгоритмически полным. А зря. Все равно на template-ах можно писать программы, которые вешают компилятор, какая тогда уж разница, пусть и препроцессор был бы помощней...

rosali

нормальный механизм для инклюда фортрановского кода

Папа говорит про другое. Если у тебя в голове накрепко засел Фортранный стиль программирования, то на любом языке, независимо от его возможностей, ты будешь статически распределять память и писать циклы вместо рекурсии...

Ivan8209

Возьми и посмотри, насколько редко обращение к указателю происходит как к ссылке, а не как к массиву. Могу тебя разочаровать --- это не настолько редкое явление.
Да и программирование задач обработки именно что массивов данных встречается не настолько редко, чтобы твой довод имел силу.
Для метапрограммирования надо брать другой язык.
Си неспособен на такое даже со своим препроцессором.
Template-ов в Си --- нет.
Алгоритмически полный препроцессор --- m4.
---
...Я работаю антинаучным аферистом...

Ivan8209

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

rosali

насколько редко обращение к указателю происходит как к ссылке, а не как к массиву

Я же тебе не о том
int a[N];
//редко. N должен быть define-ом
int * a = new int[N];
//часто. N может быть const int, а может быть вообще объектом класса с operator int
А какая разница, как потом обращаться?..

rosali

Сможешь выделить массив длины, передаваемой как параметр подпрограммы, на стеке?

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

icc
А разворачивать рекурсию?

А что это значит?

Ivan8209

Второе --- это не Си.
Это приплюснутое.
Память где выделяется?
На стеке? В куче?
Если второе, то см. malloc.
Кстати, как управлять стратегией распределения памяти в куче?
Писать кучу самому?
Как задействовать самописную кучу под видом обычной?
---
...Я работаю антинаучным аферистом...

Chupa

alloca -- на стеке
см. man 3 alloca

Ivan8209

Хвостовую рекурсию.
"int *a=new int[N]" где выделит?
На стеке?
Если да, что делать с висячими?
---
...Я работаю антинаучным аферистом...

rosali

"int *a=new int[N]" где выделит?
На стеке?

Че ты вообще пишешь? new выделит в куче ясное дело... Ну если ты не переопределил new ...

rosali

Как задействовать самописную кучу под видом обычной?

А какая для тебя "обычная"?
см. malloc

Эта? Она такая же самописная... только не тобой...
PS У тебя есть какая-то тема, которую ты сейчас обсуждаешь? Какое-то утверждение, которое доказываешь? Или просто пишешь, что знаешь?..

Ivan8209

#define --- зло.
Потому что употребляется как очень слабое восполнение недостатков самого языка программирования.
---
...Я работаю антинаучным аферистом...

buka

А мне язык без макросов кажется убогим.

Ivan8209

Семейство ML.
Некоторые Лиспы.
---
...Я работаю антинаучным аферистом...

yolki

Просматривал исходники..
Cygnus: src/gcc/cp/lex.c


#define do(X) \
{ \
t = (X); \
if (t && TREE_CODE (t) == TYPE_DECL && TREE_TYPE (t) == type) \
return t; \
}
do (IDENTIFIER_LOCAL_VALUE (node;
do (IDENTIFIER_CLASS_VALUE (node;
do (IDENTIFIER_NAMESPACE_VALUE (node;
#undef do

rosali

Дык, ты покажи эти TREE_CODE, IDENTIFIER_LOCAL_VALUE, ... а то не прорюхать...

yolki

Там ничего интересного:


#define TREE_CODE(NODE) enum tree_code) (NODE)->common.code)
#define IDENTIFIER_LOCAL_VALUE(NODE) struct lang_identifier *) (NODE->local_value)


Ну и т.п. Но не это интересно.
Вставило то, что описанный приём встретился буквально после того, как был здесь продемонстрирован.
например, ещё оттуда:


// Cygnus, src/gcc/cp/cp-tree.def
...
DEFTREECODE (IDENTITY_CONV, "identity_conv", 'e', 1)
DEFTREECODE (LVALUE_CONV, "lvalue_conv", 'e', 1)
DEFTREECODE (QUAL_CONV, "qual_conv", 'e', 1)
DEFTREECODE (STD_CONV, "std_conv", 'e', 1)
DEFTREECODE (PTR_CONV, "ptr_conv", 'e', 1)
DEFTREECODE (PMEM_CONV, "pmem_conv", 'e', 1)
DEFTREECODE (BASE_CONV, "base_conv", 'e', 1)
DEFTREECODE (REF_BIND, "ref_bind", 'e', 1)
DEFTREECODE (USER_CONV, "user_conv", 'e', 2)
DEFTREECODE (AMBIG_CONV, "ambig_conv", 'e', 1)
DEFTREECODE (RVALUE_CONV, "rvalue_conv", 'e', 1)
...


А вот пример использования:


// там же, src/gcc/cp/lex.c
#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE,
char cplus_tree_code_type[] = {
'x',
#include "cp-tree.def"
};
#undef DEFTREECODE
#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) LENGTH,
int cplus_tree_code_length[] = {
0,
#include "cp-tree.def"
};
#undef DEFTREECODE
#define DEFTREECODE(SYM, NAME, TYPE, LEN) NAME,
char *cplus_tree_code_name[] = {
"@@dummy",
#include "cp-tree.def"
};
#undef DEFTREECODE

xronik111

Сможешь выделить массив длины, передаваемой как параметр подпрограммы, на стеке?



int f(int n) {
int data[n];
...
}

NB: начиная с C99.

Ivan8209

> NB: начиная с C99.
Много ли компиляторов поддерживают это?
---
...Я работаю антинаучным аферистом...

xronik111

Не знаю, есть в gcc и icc. Да, как я понял, стандарт не заставляет компиляторы реализовывать выделение памяти для таких массивов именно на стеке, но по сути дела этот механизм введен как раз вместо alloca. Более того, одновременное использование alloca и массивов переменной длины может быть несовместимо, по крайней мере в Linux/gcc.
Оставить комментарий
Имя или ник:
Комментарий: