И еще раз про #define
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-ы. Причем по описанию ошибки и месту, на которое он указывает, нельзя догадаться, что не так.
Реальная ситуация была немного сложнее, циклическая ссылка была через промежуточные классы, много времени потратил пока разобрался.
У С# таких проблем не возникает, и это естественно - зачем мне следить за порядком описания классов.
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...
это конечно не предел мечтаний, но в Яве например и того нету. Из-за этого так популярны генераторы Явового кода. А как потом синхрониз[ ировать|овывать|овать ] (а как правильно то? ) изменения сделанные в сгенерированном и генерирующем, вообще мне плохо понятно...
ну да, я немного не в тему , просто фанаты С++ пытались это оспаривать...
Стильно. А нахрена это?
Просто не надо руками менять тот код, который генерируется автоматически, и все будет в порядке.
А нахрена это?
А вот я взял потом и с полпинка написал
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-ы так писать, но что-то в этом есть... Надо вообще в язык встраивать возможность работать с метаданными, чтобы можно было, скажем, написать цикл по всем полям структуры?..
Просто не надо руками менять
Ну это все понятно, но программисты разные бывают.. Одни воспринимают твое "не надо" как табу, а другим кажется, что в особых случаях все таки можно
Да, реально клево. Надо подумать, может, имеет смысл свойй код оформлять похожим образом
программирования, а не ставить костыли и подпорки, вылезающие в самое
неподходящее время.
---
...Я работаю антинаучным аферистом...
А чем С/С++ неприличный?
Вот я давеча объебался с #define. Переменная была определена сначала в enum {}, а потом в #define. И компилятор не выругался, что типа redefined.
Например, отсутствием операции масштабирования, из-за чего сдвиг
Бернулли производится через задницу.
Ещё отсутствием операции возведения даже в целочисленную степень.
Отсутствием приличных, постоянных констант.
Отсутствием чёткого соглашения о знаке остатка от деления.
Я могу много чего рассказать про Си.
---
...Я работаю...
Они временные что-ли?
лол тебя научить её писать?
>Я могу много чего рассказать про Си.
это безусловно минус сей, но его можно стерпеть
long a[N];
---
...Я работаю антинаучным аферистом...
---
...Я работаю антинаучным аферистом...
гнутое расширение afaik
Real Progammer can write FORTRAN in any language.
---
...Я работаю антинаучным аферистом...
Из-за чего для Си понадобились костыли наподобие его препроцессора.
---
...Я работаю антинаучным аферистом...
сначала в enum {}, а потом в #define
Как говорится, язык программирования должен позволять программисту застрелиться
И компилятор не выругался, что типа redefined
А это он вообще никак не мог бы допетрить. Препроцессор не понимает слова enum, а компилятору потом уже и нечего было понимать...
а не текстовые подстановки.
---
...Я работаю антинаучным аферистом...
Из-за чего для Си понадобился ... препроцессор
Так из-за чего, все-таки он понадобился? Из-за
long a[2**N];
Или я чего недопонял?
поймёшь, чего не хватает в Си.
Это, например: объявление констант, типов, интерфейсов,
встраиваемые подпрограммы,--- в общем, куча всего того, что в
приличных языках делается сразу, изначально присущим.
---
...Я работаю антинаучным аферистом...
>Я могу много чего рассказать про Си.
это безусловно минус сей, но его можно стерпеть
Собсна, Си предоставляет нормальный механизм для инклюда фортрановского кода.
имхо проще объектники линковать
объявление констант, типов, интерфейсов,
встраиваемые подпрограммы
Единственное отличие #define от const int, то что в некоторых реализациях второй не может быть размером массива. Массивы в С используются крайне редко (это же не фортран! ) так что этим можно пренебречь. А в остальном, есть слова typedef, inline, при чем тут препроцессор?...
Препроцессор по существу нужен для манипуляций кодом как данными. Создатели С побоялись, что на некоторых программах компилятор будет зависать и сделали препроцессор не алгоритмически полным. А зря. Все равно на template-ах можно писать программы, которые вешают компилятор, какая тогда уж разница, пусть и препроцессор был бы помощней...
нормальный механизм для инклюда фортрановского кода
Папа говорит про другое. Если у тебя в голове накрепко засел Фортранный стиль программирования, то на любом языке, независимо от его возможностей, ты будешь статически распределять память и писать циклы вместо рекурсии...
Да и программирование задач обработки именно что массивов данных встречается не настолько редко, чтобы твой довод имел силу.
Для метапрограммирования надо брать другой язык.
Си неспособен на такое даже со своим препроцессором.
Template-ов в Си --- нет.
Алгоритмически полный препроцессор --- m4.
---
...Я работаю антинаучным аферистом...
Сколько трансляторов Си умеют определять отсутствие побочных эффектов?
А разворачивать рекурсию?
---
...Я работаю антинаучным аферистом...
насколько редко обращение к указателю происходит как к ссылке, а не как к массиву
Я же тебе не о том
int a[N];
//редко. N должен быть define-ом
int * a = new int[N];
//часто. N может быть const int, а может быть вообще объектом класса с operator int
А какая разница, как потом обращаться?..
Сможешь выделить массив длины, передаваемой как параметр подпрограммы, на стеке?
Не знаю зачем, но смогу. alloca называется...
Сколько трансляторов Си умеют определять отсутствие побочных эффектов?
icc
А разворачивать рекурсию?
А что это значит?
Это приплюснутое.
Память где выделяется?
На стеке? В куче?
Если второе, то см. malloc.
Кстати, как управлять стратегией распределения памяти в куче?
Писать кучу самому?
Как задействовать самописную кучу под видом обычной?
---
...Я работаю антинаучным аферистом...
см. man 3 alloca
"int *a=new int[N]" где выделит?
На стеке?
Если да, что делать с висячими?
---
...Я работаю антинаучным аферистом...
"int *a=new int[N]" где выделит?
На стеке?
Че ты вообще пишешь? new выделит в куче ясное дело... Ну если ты не переопределил new ...
Как задействовать самописную кучу под видом обычной?
А какая для тебя "обычная"?
см. malloc
Эта? Она такая же самописная... только не тобой...
PS У тебя есть какая-то тема, которую ты сейчас обсуждаешь? Какое-то утверждение, которое доказываешь? Или просто пишешь, что знаешь?..
Потому что употребляется как очень слабое восполнение недостатков самого языка программирования.
---
...Я работаю антинаучным аферистом...
А мне язык без макросов кажется убогим.
Некоторые Лиспы.
---
...Я работаю антинаучным аферистом...
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
Дык, ты покажи эти TREE_CODE, IDENTIFIER_LOCAL_VALUE, ... а то не прорюхать...
#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
Сможешь выделить массив длины, передаваемой как параметр подпрограммы, на стеке?
int f(int n) {
int data[n];
...
}
NB: начиная с C99.
Много ли компиляторов поддерживают это?
---
...Я работаю антинаучным аферистом...
Не знаю, есть в gcc и icc. Да, как я понял, стандарт не заставляет компиляторы реализовывать выделение памяти для таких массивов именно на стеке, но по сути дела этот механизм введен как раз вместо alloca. Более того, одновременное использование alloca и массивов переменной длины может быть несовместимо, по крайней мере в Linux/gcc.
Оставить комментарий
rosali
Сколько бы ни ругали C-препроцессор, он останется гениальным изобретением .Вот код одной из одной библиотеки:
Кто-то уже догадался, как воспользоваться таким странным определением enum-а?