Можно ли сделать макрос из #define ?

Модератор: Модераторы разделов

MiK13
Сообщения: 1301
ОС: Linux Debian

Можно ли сделать макрос из #define ?

Сообщение MiK13 »

Здравствуйте!
В программе приходится работать с различными битами-флагами. Для этого использую операторы #define, которые объединяются в следующие группы:

Код: Выделить всё

#define BIT_PAR n          // n -- целое число от 0 до 31
#define FLAG_PAR (1<<BIT_PAR)
#define FLAG_PAR_t (var_flags&FLAG_PAR)
#define FLAG_PAR_i (var_flags^=FLAG_PAR)
#define FLAG_PAR_s (var_flags|=FLAG_PAR)
#define FLAG_PAR_c (var_flags&=~FLAG_PAR)

После этого я могу простым оператором типа FLAG_PAR_x проверить, инвертировать, установить или сбросить нужный мне флаг.
Проблема в том, что таких флагов много, несколько десятков. И располагаются они в нескольких переменных. В результате листинг .h-файла сильно раздувается. И потом возникают некоторые сложности, когда нужно добавить ещё один флаг или найти нужный, Поэтому хотелось бы эти группы из 7 строк (включая пустую строку между группами для лучшей читабельности) заменить одной строкой, типа
FLAGS(var,PAR,n)
которая бы при компиляции расширялась в 6 указанных выше строк. Но что-то я никак не могу сообразить, как это сделать.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Можно ли сделать макрос из #define ?

Сообщение drBatty »

MiK13
вообще-то для этой цели существуют поля. типа:

Код: Выделить всё

struct FIELDS {
 unsigned x1 :1;
 unsigned x2 :1;
 ...
};

только используйте именно unsigned, простой int :1 принимает значения 0 и МИНУС 1. А вам наверное это не нужно.
Если хотите обращаться и к полям, и целиком к числу - используйте объединения union {};

А #define использовались в прошлом веке, их читать/писать/отлаживать неудобно.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
MiK13
Сообщения: 1301
ОС: Linux Debian

Re: Можно ли сделать макрос из #define ?

Сообщение MiK13 »

Структуры с битовыми полями я использую.
Но уже много написано, и заменять по разным текстам FLAG_PAR_i; на что-то типа dd->flags_1.PAR^=1; довольно сложно.
И я бы не сказал, что второй вариант нагляднее.

Но дело не только в этом. А ещё и в том, что с помощью макросов, расширяющихся в группу из 6 дефайнов я мог бы в одном месте собрать все флаги и мог бы легко добавлять новые или изменять старые
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Можно ли сделать макрос из #define ?

Сообщение drBatty »

MiK13 писал(а):
23.09.2011 14:00
И я бы не сказал, что второй вариант нагляднее.

это С++? тогда сделайте метод FLAG_PAR_i();


MiK13 писал(а):
23.09.2011 14:00
А ещё и в том, что с помощью макросов, расширяющихся в группу из 6 дефайнов я мог бы в одном месте собрать все флаги и мог бы легко добавлять новые или изменять старые


к сожалению, насколько я помню, это невозможно, ибо недопустим вложенный #define
но вы можете использовать поля в define

хотя... я-бы не стал. да и вообще все эти побитывые операции очень много времени занимают. Попробуйте без них - всё станет быстрее раз в цать. Ну обычно так бывает, если сменить 1 бит на один int. Ну а затраты памяти возрастают незначительно. Большие битовые карты можно хранить упакованными, и распаковывать в поля типа int[] по мере надобности.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
shotdownsystem
Сообщения: 423
ОС: Basic command interpreter

Re: Можно ли сделать макрос из #define ?

Сообщение shotdownsystem »

MiK13 писал(а):
23.09.2011 13:23
FLAGS(var,PAR,n)
которая бы при компиляции расширялась в 6 указанных выше строк. Но что-то я никак не могу сообразить, как это сделать.

А вот так не пойдет ?
#define FLAGS_t (var,n) (var&(1<<n))
есть еще оператор ##, т.е.
FLAGS(var,PAR,n) FLAGS_##PAR (var,n)
Сформулируйте требования, что в конце должно получится ? Потому как это все попахивает...
puts ("Working, please wait...");while(1);
Спасибо сказали:
MiK13
Сообщения: 1301
ОС: Linux Debian

Re: Можно ли сделать макрос из #define ?

Сообщение MiK13 »

shotdownsystem писал(а):
23.09.2011 15:13
Сформулируйте требования, что в конце должно получится ?
Требования я написал в первом посте. Я хочу сделать текст более компактным. Т.е. мне нужно, чтобы я мог заменить шесть строк одной, чтобы было легко находить изменять и добавлять нужный флаг. И, соответственно, операторы для работы с ним
Спасибо сказали:
MiK13
Сообщения: 1301
ОС: Linux Debian

Re: Можно ли сделать макрос из #define ?

Сообщение MiK13 »

drBatty писал(а):
23.09.2011 15:07
это С++? тогда сделайте метод FLAG_PAR_i();
Нет, это обычный C.
drBatty писал(а):
23.09.2011 15:07
к сожалению, насколько я помню, это невозможно, ибо недопустим вложенный #define
Вот это меня и огорчает
drBatty писал(а):
23.09.2011 15:07
хотя... я-бы не стал. да и вообще все эти побитывые операции очень много времени занимают.
А почему много? По-моему, любой оператор типа a&b, a|=b должен транслировать в 1-2, ну, может быть, 3 машинные инструкции
Спасибо сказали:
shotdownsystem
Сообщения: 423
ОС: Basic command interpreter

Re: Можно ли сделать макрос из #define ?

Сообщение shotdownsystem »

MiK13 писал(а):
23.09.2011 16:37
shotdownsystem писал(а):
23.09.2011 15:13
Сформулируйте требования, что в конце должно получится ?
Требования я написал в первом посте. Я хочу сделать текст более компактным. Т.е. мне нужно, чтобы я мог заменить шесть строк одной, чтобы было легко находить изменять и добавлять нужный флаг. И, соответственно, операторы для работы с ним

Это не требования, а пожелания. Можете сформулировать задачу целиком ?
У каждого флага свой набор операторов? Или они справедливы для всех флагов в равной степени ?
puts ("Working, please wait...");while(1);
Спасибо сказали:
shotdownsystem
Сообщения: 423
ОС: Basic command interpreter

Re: Можно ли сделать макрос из #define ?

Сообщение shotdownsystem »

MiK13 писал(а):
23.09.2011 16:53
drBatty писал(а):
23.09.2011 15:07
хотя... я-бы не стал. да и вообще все эти побитывые операции очень много времени занимают.
А почему много? По-моему, любой оператор типа a&b, a|=b должен транслировать в 1-2, ну, может быть, 3 машинные инструкции

Есть мнение, что в си операции с структурами битовых полей разворачиваются в что-то страшное. Но на уровне слухов. Кстати, может кто подкинет ссылку, во что они разворачиваются, миф или нет?
puts ("Working, please wait...");while(1);
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Можно ли сделать макрос из #define ?

Сообщение drBatty »

MiK13 писал(а):
23.09.2011 16:37
Требования я написал в первом посте. Я хочу сделать текст более компактным. Т.е. мне нужно, чтобы я мог заменить шесть строк одной, чтобы было легко находить изменять и добавлять нужный флаг. И, соответственно, операторы для работы с ним

может проще нормально писать(не так), и/или использовать мощный редактор?
MiK13 писал(а):
23.09.2011 16:53
А почему много? По-моему, любой оператор типа a&b, a|=b должен транслировать в 1-2, ну, может быть, 3 машинные инструкции

потому-что процессоры 32х и 64и битные, и заставлять их работать с битами - нерационально. Они всё равно будут работать с машинным словом. Как-бы красиво вы это не заворачивали в define/class/template.
shotdownsystem писал(а):
23.09.2011 17:38
Есть мнение, что в си операции с структурами битовых полей разворачиваются в что-то страшное. Но на уровне слухов. Кстати, может кто подкинет ссылку, во что они разворачиваются, миф или нет?

ну возьмите дизассемблер и посмотрите. Разворачиваются. Я гарантирую это ;)
Можете также и использовать метод тыка - протестируйте.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
MiK13
Сообщения: 1301
ОС: Linux Debian

Re: Можно ли сделать макрос из #define ?

Сообщение MiK13 »

shotdownsystem писал(а):
23.09.2011 17:28
У каждого флага свой набор операторов? Или они справедливы для всех флагов в равной степени ?
Т.к. флаги однобитные, то со всеми могут быть выполнены 4 операции: проверить, установить в 1, установить в 0 и инвертировать. Поэтому я сначала и стал делать для каждого флага группу из 4-х операторов. Легко добавлять -- просто копирую группу строк в .h-файле и заменяю маску флага и его название.
Но когда флагов стало много, то стало трудновато их искать, особенно если надо, вдруг, перенести бит с одного места на другое. Вот и возникла мысль: а нельзя ли, используя макросредства каждую такую группу заменить одной строкой? Но, похоже, нельзя. Разве что написать свой дополнительный препроцессор, который будет разворачивать подобные макросы.

А по поводу быстродействия, я провёл такой эксперимент.
Написал простую программу:

Код: Выделить всё

struct {
  int a:17;
  int b:1;
  int c:10;
} a1;
int a2;
int a0;
main() {
  a0=123456;
  a1.b=1;
  a0=654321;
  a2|=1<<17;
  a0=777777;
}
и посмотрел, что будет после gcc -S c.c. Получилось:

Код:

.file "c.c" .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $4, %esp movl $123456, a0 movzbl a1+2, %eax orl $2, %eax movb %al, a1+2 movl $654321, a0 movl a2, %eax orl $131072, %eax movl %eax, a2 movl $777777, a0 addl $4, %esp popl %ecx popl %ebp leal -4(%ecx), %esp ret .size main, .-main .comm a1,4,4 .comm a2,4,4 .comm a0,4,4 .ident "GCC: (Debian 4.3.2-1.1) 4.3.2" .section .note.GNU-stack,"",@progbits

Оба оператора, и a1.b=1; и a2|=1<<17; протранслировась в три машинные команды: скопировать значение из памяти в регистр, выполнить операцию OR между регистром и константой и скопировать значение из регистра назад в память. Только в первом случае операция выполнялась с одним байтом, а во втором -- со всем 32-битным словом. Думаю, что скорость выполнения в обоих случаях будет одинаковой
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Можно ли сделать макрос из #define ?

Сообщение drBatty »

MiK13
а если данные будут иметь размер одно машинное слово? например оператор проверки вырождается в AND EAX,EAX, т.е. на первом пне обычно всего 1/2 такта, а не три неспариваемых команды как у вас. Т.е. быстродействие даже на первом пне раз в 6 больше. А на нормальном CPU отрыв будет ещё больше.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
shotdownsystem
Сообщения: 423
ОС: Basic command interpreter

Re: Можно ли сделать макрос из #define ?

Сообщение shotdownsystem »

MiK13 писал(а):
23.09.2011 18:08
А по поводу быстродействия, я провёл такой эксперимент.

спасибо, миф разрушен, ничего сверх страшного, все ожидаемо. ))

по теме.
#define BIT0 (1<<0)
#define BIT1 (1<<1)
...
#define BITN (1<<N)



#define SET_BIT(__bit,__value) (__value|=__bit)



SET_BIT (BIT1,foo);


а так что не так?
puts ("Working, please wait...");while(1);
Спасибо сказали:
shotdownsystem
Сообщения: 423
ОС: Basic command interpreter

Re: Можно ли сделать макрос из #define ?

Сообщение shotdownsystem »

drBatty писал(а):
23.09.2011 18:19
а если данные будут иметь размер одно машинное слово? например оператор проверки вырождается в AND EAX,EAX

ну а так AND EAX,ECX. и возможность проверить несколько флагов... впрочем да, есть и минусы.
Но автор задачу целиком нам не говорит, значит знает что делает..
puts ("Working, please wait...");while(1);
Спасибо сказали:
MiK13
Сообщения: 1301
ОС: Linux Debian

Re: Можно ли сделать макрос из #define ?

Сообщение MiK13 »

shotdownsystem писал(а):
23.09.2011 19:22
MiK13 писал(а):
23.09.2011 18:08
А по поводу быстродействия, я провёл такой эксперимент.

спасибо, миф разрушен, ничего сверх страшного, все ожидаемо. ))
Думаю, что сложности могут быть, если нужно работать с i-м битом, причём i -- переменная, а не константа. Хотя тоже думаю, что ничего страшного нет.
shotdownsystem писал(а):
23.09.2011 19:22
по теме.
#define BIT0 (1<<0)
#define BIT1 (1<<1)
...
#define BITN (1<<N)

#define SET_BIT(__bit,__value) (__value|=__bit)

SET_BIT (BIT1,foo);
В принципе можно. Только хотелось бы, чтобы #define объединял номер бита (или маску) и переменную, в которой он находится.
Хотя... может быть можно сделать что-то вроде
#define BIT1A (1<<1),A
Ну и потом просто 4 макроса: для проверки, установки, сброса и инверсии.

Код: Выделить всё

#define BIT_s(__bit,__value) (__value|=__bit)
#define BIT_c(__bit,__value) (__value&=~__bit)
#define BIT_t(__bit,__value) (__value&__bit)
#define BIT_i(__bit,__value) (__value^=__bit)

Но всё равно придётся во всех файлах заменять мою конструкцию типа
BIT1A_x на BIT_x(BIT1A)
Впрочем, это уже не так и сложно. Может быть и попробую так сделать.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Можно ли сделать макрос из #define ?

Сообщение drBatty »

shotdownsystem писал(а):
23.09.2011 19:41
и возможность проверить несколько флагов...

макросами из первого поста?
shotdownsystem писал(а):
23.09.2011 19:41
Но автор задачу целиком нам не говорит, значит знает что делает..

не факт.

shotdownsystem писал(а):
23.09.2011 19:22
спасибо, миф разрушен, ничего сверх страшного, все ожидаемо. ))

просто это очень медленно. хотя страшного да - ничего нет.
MiK13 писал(а):
23.09.2011 20:24
Думаю, что сложности могут быть, если нужно работать с i-м битом, причём i -- переменная

не важно. есть команда сдвига не на константу, а на регистр cl. В x86.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
shotdownsystem
Сообщения: 423
ОС: Basic command interpreter

Re: Можно ли сделать макрос из #define ?

Сообщение shotdownsystem »

drBatty писал(а):
23.09.2011 23:16
макросами из первого поста?

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

drBatty писал(а):
23.09.2011 23:16
не важно. есть команда сдвига не на константу, а на регистр cl. В x86.

да, но если у нас набор констант, то сдвигать ничего не надо, все уже сдвинуто, до нас.
puts ("Working, please wait...");while(1);
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Можно ли сделать макрос из #define ?

Сообщение drBatty »

shotdownsystem писал(а):
23.09.2011 23:27
автор убедился, что его макросы не айс и поэтому обратился за помощью

наоборот - автору макросов мало, и он хочет макросы в макросах. Мало того, у него и так индусский код множится, дык он ещё и желает множить индусский код макросами. Ибо лень. :(
shotdownsystem писал(а):
23.09.2011 23:27
да, но если у нас набор констант, то сдвигать ничего не надо, все уже сдвинуто, до нас.

ну это вообще-то выполняется на этапе компиляции даже без оптимизации. В рантайме 1<<17 никто считать не станет. А процессору в принципе всё равно на что сдвигать - на константу, или на регистр. На регистр даже побыстрее немного.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
shotdownsystem
Сообщения: 423
ОС: Basic command interpreter

Re: Можно ли сделать макрос из #define ?

Сообщение shotdownsystem »

drBatty писал(а):
23.09.2011 23:34
shotdownsystem писал(а):
23.09.2011 23:27
автор убедился, что его макросы не айс и поэтому обратился за помощью

наоборот - автору макросов мало, и он хочет макросы в макросах. Мало того, у него и так индусский код множится, дык он ещё и желает множить индусский код макросами. Ибо лень. :(


макросы в макросах - это нормально, для многоархитектурности и еще много чего. Linux kernel sources посмотрите или там тоже "индусский код"?
puts ("Working, please wait...");while(1);
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Можно ли сделать макрос из #define ?

Сообщение drBatty »

shotdownsystem
я про другое говорю.
Вложенных макросов в С не бывает. А в ядре совсем другое - там объявленны разные макроопределения в зависимости от архитектуры.

#if условие
#define x y
#Endif
можно

#define x #define y z
нельзя
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
MiK13
Сообщения: 1301
ОС: Linux Debian

Re: Можно ли сделать макрос из #define ?

Сообщение MiK13 »

MiK13 писал(а):
23.09.2011 20:24
Хотя... может быть можно сделать что-то вроде
#define BIT1A (1<<1),A
Ну и потом просто 4 макроса: для проверки, установки, сброса и инверсии.

Код: Выделить всё

#define BIT_s(__bit,__value) (__value|=__bit)
#define BIT_c(__bit,__value) (__value&=~__bit)
#define BIT_t(__bit,__value) (__value&__bit)
#define BIT_i(__bit,__value) (__value^=__bit)

К сожалению, не получилось :(
На оператор
BIT_s(BIT1A);
компилятор выдал:
macro "BIT_s" requires 2 arguments, but only 1 given
Т.е., похоже, что через #define определить имя, которое бы интерпретировалось как два аргумента, нельзя
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Можно ли сделать макрос из #define ?

Сообщение drBatty »

MiK13 писал(а):
24.09.2011 18:59
Т.е., похоже, что через #define определить имя, которое бы интерпретировалось как два аргумента, нельзя

ага. нельзя.
но вы можете использовать усовершенствованные макросы С++ == шаблоны:
Шаблоны - усовершенствованные макросы

На сцену выходит механизм шаблонов - усовершенствованный макропроцессор для директив #define. Шаблоны представляют собой ничто иное, как макросы без всех перечисленных ограничений. Они могут быть вложенными. Вам не придется беспокоиться о дублировании их функций. Большинство отладчиков C++ при возникновении ошибки правильно указывает строку шаблона. Размер шаблона не вызовет никаких проблем. Наконец, вам не придется уродовать свою прекрасную программу закорючками вроде \ и ##.

(С) Джефф Элджер. C++ ++
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали: