Страницы

Поиск по вопросам

Показаны сообщения с ярлыком макросы. Показать все сообщения
Показаны сообщения с ярлыком макросы. Показать все сообщения

вторник, 17 марта 2020 г.

САмостоятельное нажатие клавиши в игре

#макросы


Есть игра в которой каждые 5 - 10 секунд респауниться какой то предмет... Мне его
нужно забирать, для этого нужно нажать клавишу допустим  "F" но это нужно делать очень
и очень долго что бы собрать много и продать... Вот у меня такой вопрос, а можно запрограммировать
клавишу F на самостоятельное нажатие через каждые 5 секунд... Что бы можно было запустить
игру и пойти на работу или просто куда угодно, аоставить только игру включённой и что
бы она сама все собирала?
    


Ответы

Ответ 1



Как раз для таких вещей есть AutoIt.

Ответ 2



Вероятно вам нужен автокликер или же нечто подобное. Посмотрите "Автокликер для клавиатуры" в гугле и пользуйтесь на здоровье) Удачи на "полях сражений" ;)

Ответ 3



Можно. Напрмиер, встроенный в Windows язык VBS: Do sendkeys('f') sleep(5000) Loop Сохраните под названием fkey.vbs Создайте ярлык на этот файл и укажите в свойствах этого ярлыка быстрый запуск(например, ctrl+alt+c) Запускаете вашу игру и нажимаете указанную ранее комбинацию клавиш Profit.

воскресенье, 9 февраля 2020 г.

Зачем в библиотеках используются макросы вместо языковых конструкций?

#cpp #библиотеки #макросы


Объясните, пожалуйста, зачем в стандартной библиотеке, в библиотеках boost и во многих
других библиотеках некоторые ключевые слова, названия пространств имен, функций и т.п.
оформляются в виде макросов? Например, _XSTD, NOEXCEPT, THROW, and и еще многие другие.
При этом имеются ввиду т.н. «рабочие» макросы, т.е. те, которые взаимодействуют с основным
кодом на C++, а не что-то платформенное типа _MSC_VER или __WIN__. Хотелось бы услышать
в ответе некоторые причины/варианты использования в библиотеках макросов.
    


Ответы

Ответ 1



Что касается использования макросов вида THROW, CATCH и т.п., то такие вводятся для возможности отключить механизм исключений при необходимости (пустое определение). Макросы вида NOEXCEPT, DEFAULT и т.п. вводятся с той целью, чтобы бибилотека могла работать с более старыми версиями компиляторов. Например, в boost имеется config-файл, в котором содержатся директивы #define, определяющие или не определяющие, например, BOOST_HAS_CXX_RVALUE_REFERENCES или BOOST_HAS_NOEXCEPT - это зависит от версии компилятора. Далее, #ifndef-ом определяется соответствующий макрос - либо вводя его как альтернативу, например, тому же noexcept, либо делая его «пустышкой».

Ответ 2



Мало ли как придется в новой версии объявлять функции? Вдруг изменится какой-то тип? Или, например, реальная ситуация - изменение спецификации исключений в новом стандарте. Или эти исключения просто нужно будет отключить? Или сейчас компилятор понимает throw(), но пока не понимает noexcept? Словом, максимально обезопасить себя от будущих неприятностей - чтобы при необходимости внесения изменений внести их в минимальном объеме. При этом еще и нужно сделать так, чтобы это работало и в C, и в C++. Да еще и разрабатывалось это все лет 30 назад, а то и раньше. Вот так и стали использоваться именно макросы - как наиболее отвязанное от языка и доступное в те времена, когда еще и C++ не было :) средство. "По-моему, так." (с) Пух

пятница, 24 января 2020 г.

Использование в макросах строковых литералов в качестве типов

#cpp #шаблоны_с++ #макросы #рефлексия


В классе есть метаинформация, представленная в виде вектора пар значений строка-строка,
которые представляют собой строковое представление типа и имени члена класса.

Напрмиер:

class TObject
{
private:
    int  t_fMember;
    char t_sMember;
};


Для данного класса метаинформация представляется следующим множеством пар:

{
    {"int", "t_fMember"},
    {"char","t_sMember"}
}


Есть макрос, которые скрывает за собой шаблонную ф-ию:

#define GET_MEMBER_VALUE( MEM_TYPE, ... ) \
template  \
MEM_TYPE RetVal(...)\
{\
\
}


В сигнатуре и возвращаемом типе шаблонной ф-ии я писал не MEM_TYPE, а ##MEM_TYPE,
надеясь, что, например "int" превратится в int, однако, этого не произошло (чего и
следовало ожидать).

Есть ли способ проводить подобную конверсию ? Передавать тип как строковый литерал,
а затем делать обратное преобразование ? ("type" -> type)

UPD0: как бы я хотел это использовать:

#define GET_MEMBER_VALUE( MEM_TYPE, MEM_NAME, PTR_TO_OBJECT ) \
    template  \
    MEM_TYPE RetVal(...)\
    {\
        return PTR_TO_OBJECT->##MEM_NAME; \
    }


Отдаем указатель на объект, тип члена и его имя, обращаемся оператором -> к члену
по имени и возвращаем его значение.
    


Ответы

Ответ 1



Очевидно, из строки с именем нельзя получить указатель на член класса, просто потому что в предложенной схеме хранения метаинформации отсутствую указатели на члены классов. Более развитые системы хранения метаинформации (например, BOOST.FUSION) это умеют. #include #include #include #include #include #include #include struct TObject { int t_fMember; char t_sMember; } ; BOOST_FUSION_ADAPT_STRUCT( TObject, (int , t_fMember) (char, t_sMember) ); template struct memberByNameVisitor { ObjectType* ptr; const char* seek_name; ReturnType** result; template void visit_field( const char* field_name, T& field_value ) const {} void visit_field( const char* field_name, ReturnType& field_value ) const { if( strcmp( seek_name, field_name) ==0 ) *result = & field_value; } template void operator() (Index idx) const { visit_field( boost::fusion::extension::struct_member_name::call(), boost::fusion::at(*ptr) ); } }; template ReturnType& memberByName( ObjectType& ref, const char* name ) { typedef boost::mpl::range_c::value > Indices; ReturnType* result=nullptr; memberByNameVisitor visitor={&ref, name, &result}; boost::fusion::for_each( Indices(), visitor ); if(result!=nullptr) return *result; else throw std::invalid_argument("Filed is not found"); } int main(int argc, char *argv[]) { TObject test ={ 1234, 'A' }; std::cout << memberByName( test, "t_fMember" ) << std::endl; memberByName(test, "t_sMember") = 'C'; std::cout << memberByName(test, "t_sMember") << std::endl; return 0; }

суббота, 11 января 2020 г.

Печать значений макросов в читаемом виде

#cpp #макросы


Условный пример, есть группы макросов (таких групп около 20 по проекту + неизвестное
количество из винапи): 

#define DTR_CONTROL_DISABLE    0x00
#define DTR_CONTROL_ENABLE     0x01
#define DTR_CONTROL_HANDSHAKE  0x02


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

DtrControl: [0х01] DTR_CONTROL_ENABLE


Текущая реализация: функции to_str, внутри у каждой из которых switch
блок, вида:

switch (dcb.fDtrControl)
{
case DTR_CONTROL_DISABLE:   tmp = "DTR_CONTROL_DISABLE";   break;
case DTR_CONTROL_ENABLE:    tmp = "DTR_CONTROL_ENABLE";    break;
case DTR_CONTROL_HANDSHAKE: tmp = "DTR_CONTROL_HANDSHAKE"; break;
default: tmp = QString("Unknown [%1]").arg(dcb.fDtrControl);
}


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


Ответы

Ответ 1



Си - не мой основной язык, поэтому мой вариант может быть субоптимальным, но можно сделать нечто подобное на макросах: #include #define FOO_1 1 #define FOO_2 2 #define STR_(x) #x #define STR(x) STR_(x) #define FOO_STR(x) "[" STR(x) "] " #x int main(void) { printf("%s\n", FOO_STR(FOO_1)); printf("%s\n", FOO_STR(FOO_2)); return 0; } Вывод: [1] FOO_1 [2] FOO_2

воскресенье, 5 января 2020 г.

Определения макросов

#c #динамические_массивы #макросы #макрос


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


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


В методичке ничего не написано кроме пояснений про #define, #if и т.д. Хотелось бы
иметь хоть какое-то представление, о чем речь идет)
    


Ответы

Ответ 1



Похоже, что от вас хотят что-то вроде int total = 0; int mallocs = 0; int frees = 0; #define malloc(s) (mallocs++, total += (s), malloc((s))) #define free(s) (frees++, free((s))) int main(int argc, const char * argv[]) { char * c = malloc(200); char * v = malloc(2000); free(c); printf("Alloc %d bytes in %d mallocs; frees: %d times\n", total, mallocs,frees); }

пятница, 3 января 2020 г.

Раскрытие макросов

#cpp #макросы


Есть маленький вопрос по раскрытиям макросов. 

Например, этот код работает:

#define DEF_STRUCT(name, type) struct name {using t = type;};
DEF_STRUCT(hah, int)


И это:

#define DEF_STRUCT(name, type) struct name {using t = type;};
#define BUILD_STRUCT_1(name, type) DEF_STRUCT(name, type)
BUILD_STRUCT_1(heh, char) 


А вот этот ругается (Требуется спецификатор):

#define DEF_STRUCT(name, type) struct name {using t = type;};
#define BUILD_STRUCT_1(name, type) DEF_STRUCT(name, type)
#define BUILD_STRUCT_2(name, type, ...) DEF_STRUCT(name, type) BUILD_STRUCT_1(__VA_ARGS__)
BUILD_STRUCT_2(lol, bool, kek, unsigned) // Извини, не хочу работать


Вопрос: как он раскрывается и что следует поменять чтобы заработало как хотелось
(Объявление 2-х структур подряд)? 
    


Ответы

Ответ 1



Код прекрасно компилируется GCC. В MSVC он не компилируется из-за нестандартного поведения препроцессора. Эту разницу в поведении препроцессора можно проиллюстрировать примером без __VA_ARGS__ #define M2(a, b) #define M1(x) M2(x) /* <- Считается ошибкой в MSVC */ #define A 1, 2 M1(A) В качестве workaround можно предложить #define BUILD_STRUCT_2(name, type, name2, ...) \ DEF_STRUCT(name, type) BUILD_STRUCT_1(name2, __VA_ARGS__) или более общее #define FIRST(x, ...) x #define TAIL(x, ...) __VA_ARGS__ #define BUILD_STRUCT_2(name, type, ...) \ DEF_STRUCT(name, type) BUILD_STRUCT_1(FIRST(__VA_ARGS__), TAIL(__VA_ARGS__)) Хотя не совсем понятно, зачем здесь ... и __VA_ARGS__ вообще.

понедельник, 30 декабря 2019 г.

Автогенерация кода

#lisp #метапрограммирование #макросы


Подскажите, пожалуйста, на каком из языков программирования можно написать программу,
которая способна создать другую программу, а так же полностью изменить себя? 
Какие способы есть для этого, макросы, метапрограммирование? Чем тогда отличаются
макросы ассемблера от макросов Lisp?    


Ответы

Ответ 1



Подскажите пожалуйста на каком из языков программирования можно написать программу которая способна создать другую программу, а так же полностью изменить себя? Для использования возможностей метапрограммирования достаточно наличия в языке eval. Возможность построить и выполнить произвольный код который будет влиять на текущий контекст — мощное средство с помощью которого можно писать программы, пишущие программы. Чтобы "изменять себя" достаточно переопределять существующий код в рантайме. Это можно сделать много где: Smalltalk, Python, Ruby, JavaScript. Иногда много средств для изменения кода на лету доступно из без eval, когда среда поддерживает рефлексию. (Smalltalk) Какие способы есть для этого, макросы, метапрограммирование? Чем тогда отличаются макросы ассемблера от макросов Lisp? Макросы Lisp — это просто удобное средство для преобразования AST перед компиляцией ничего больше. Разница в том, что макросы asm и C-предпроцессора оперируют кусками текста. Макросы Lisp оперируют выражениями. Кроме того, макросы в Lisp -- полноценные программы, и с помощью них можно строить весьма сложные выражения. UPD: eval — это процедура которая принимает исходный код в виде данных (просто строку или AST) и выполняет его. Например в JavaScript можно написать так: eval("function some_func() { return 1; }") Здесь код передан как простой текст, но после выполнения будет объявлена функция some_func в текущем контексте, и мы сможем ею оперировать. some_func() // вернёт 1 Так как передаётся простой текст, то мы можем его преобразовывать любыми способами, и в итоге выполнить произвольный код в рантайме. AST — abstract syntax tree, такая структура данных (дерево) в которую преобразуется исходный код какого-либо языка после парсинга. После того как получено AST обычно над ним производят ряд преобразований (оптимизаций), а потом компилируют в машинный код. В случае Lisp из-за простоты синтаксиса AST будет обычный линейный список, например код: (some-construction (for i 0) ((i) exit)) В виде списка (AST) будет почти так же: (list 'some-construcion (list 'for 'i 0) (list (list 'i) 'exit)) ;; Или достаточно закавычить, чтобы получить тот же список '(some-construction (for i 0) ((i) exit)) Макросы — просто программы которые оперируют этой структурой данных, уже после того как код был прочтён, но ещё не оптимизирован и не скомпилирован. Они позволяют легко встраивать новые конструкции в язык, которые потом преобразуются в какие-то стандартные конструкции. Полезно взглянуть на язык Nemerle, который также поддерживает макросы, но не является лиспоподобным.

Ответ 2



Почти у всех уже установлен .NET Framework, в котором есть компилятор C#. Можно написать программу, которая генерирует код C# и передаёт его компилятору. Добавлено: Ещё один вариант изучаю: #include #include using namespace std; int main() { unsigned char data[]={0xb8, 0x9a, 0x02, 0x00, 0x00, 0xc3}; int(*function)()=(int(*)())VirtualAlloc(0, sizeof(data), MEM_COMMIT, PAGE_EXECUTE_READWRITE); memcpy(function, data, sizeof(data)); cout << function(); VirtualFree(function, 0, MEM_RELEASE); return 0; } Можно создавать функцию прямо из машинного кода. Массив data - это машинный код функции возвращающей число 666. Но ОС не позволит выполнить этот код, поэтому надо выделить область памяти, которую можно выполнять и скопировать туда нашу функцию. То есть функции можно создавать прямо на лету, но такой способ точно не для новичков. Я даже сам не знаю, как таким способом создать хотя бы "Hello, World!". К тому же будет работать только на процессорах архитектуры x86.

Ответ 3



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

понедельник, 23 декабря 2019 г.

Препроцессор: превратить __LINE__ в строку

#cpp #c #макросы #препроцессор


Навеяно этим вопросом.

Все время от времени пользуются отладочным выводом с предопределенными макросами типа

cout << __FILE__ << " line " << __LINE__ << endl;


Задумался - как написать макрос FILELINE, чтоб эту строчку выше заменить на

cout << FILELINE << endl;


Ну т.е. чтоб макрос раскрывался в строку, в составе которой было число, в которую
раскрывается __LINE__. Как ни кручусь - все время или число оказывается не в кавычках,
или в кавычках оказывается "__LINE__".

Ну не может быть, чтоб эта задача была неразрешима. Ткните носом, о чем я не подумал?
    


Ответы

Ответ 1



Перевод ответа с английского StackOverflow: Вам нужно сделать это в несколько этапов: #define S1(x) #x #define S2(x) S1(x) #define LOCATION __FILE__ " : " S2(__LINE__) Почему? Краткий ответ: Вам нужно раскрыть макрос __LINE__, прежде чем передавать его в #x. Развернутый ответ: Во-первых, используя оператор # в функционально-подобном макросе, он должен сопровождаться параметром макроса, но __LINE__ не является параметром, поэтому компилятор жалуется, что он является ошибочным оператором. Во-вторых, __LINE__ сам по себе является макросом и содержит номер текущей строки, его следует раскрыть до числа перед использованием его с #, в противном случае вы получите строку "__LINE __" вместо числа. Макрос S2 (__LINE__) расширяет __LINE__ до номера строки, затем мы передаем номер строки в #x.

Ответ 2



Таки получилось: #define S(x) #x #define S_(x) S(x) #define S__LINE__ S_(__LINE__) #define FILELINE (__FILE__ " line " S__LINE__) https://stackoverflow.com/questions/5641427/how-to-make-preprocessor-generate-a-string-for-line-keyword

понедельник, 25 ноября 2019 г.

Зачем нужна конструкция do { … } while(0)?


В библиотеке freeglut в файле freeglut_callbacks.c нашел такой код:

/*
 * All of the callbacks setting methods can be generalized to this:
 */
#define SET_CALLBACK(a)                                         \
do                                                              \
{                                                               \
    if( fgStructure.CurrentWindow == NULL )                     \
        return;                                                 \
    SET_WCB( ( *( fgStructure.CurrentWindow ) ), a, callback ); \
} while( 0 )


Не понимаю, зачем здесь нужен цикл, ведь он в любом случае выполниться всего один раз, разве нет?
    


Ответы

Ответ 1



Конструкция широко используется в "функциональных" макросах и предназначена для объединени нескольких statement и объявлений внутри макроса в один составной statement. Такое объединени нужно для того, чтобы макрос можно было использовать как обычный вызов функции в ветках условного оператора, циклах и т.п. без необходимости постоянно заключать этот вызов во внешнюю пару { ... } На первый взгляд того же самого можно было бы достичь просто путем заключения самог определения макроса в пару { ... } вместо do { ... } while (0). Однако вариант с { ... } будет обладать одним неприятным недостатком - надо будет постоянно помнить, что его тело представляет из себя { ... }, а после { ... } в С не всегда можно ставить ;. Например, если бы тело SET_CALLBACK было заключено просто в { ... }, то вот тако естественно выглядящий код не скомпилировался бы по причине того, что из-за поставленной после SET_CALLBACK(foo) точки с запятой ветка else оказалось оторванной от своего if if (true) SET_CALLBACK(foo); // <- "Лишняя" `;` else // <- Ошибка: оторванный `else` /* что-то еще */; Вот тут-то на сцену и выходит do { ... } while (0). Цикл do/while уникален тем, чт является единственной (почти) в языке С грамматической конструкцией, которая формируе блок, и при этом всегда безусловно заканчивается на ;. Благодаря этой замыкающей ; м можем более естественным образом использовать do { ... } while (0) внутри составных макросов для объединения их в единый statement. В вышеприведенном примере с if ;, указанная после SET_CALLBACK(foo), будет "поглощена" конструкцией do { ... } while (0) и не приведет в "разрушению" целостности if. Вот именно ради того, чтобы иметь возможность "вызывать" функциональные макросы чере тот же естественный синтаксис, через который мы вызываем обычные функции, тело составного макроса и заключают не в { ... }, а именно в do { ... } while (0) P.S. Почему авторы языка в свое время решили настоять на включении [теоретическ совсем не нужной] замыкающей ; в синтаксис do/while - не совсем ясно, но это было сделано.

Ответ 2



Это очень полезная конструкция. Так как макросы подставляются "как есть", то могут происходить различные чудные вещи. Допустим у нас есть такой макрос #define foo(x) int x;\ x=1;\ printf("%d\n",x); (пример надуманный) И если теперь вызвать вот так if (...) foo(a); то это развернется в такое if (...) int a; a=1; printf("%d\n",a); и естественно, будет работать совсем не так, как того ожидает применяющий - объявление переменной явно не там и скобок не хватает. В принципе можно было бы обойтись просто фигурными скобками, но конструкция с do-while(0 дает один существенный плюс - возможность использовать break для выхода с макроса, что ещё больше приближает макрос к обычной функции.

пятница, 1 марта 2019 г.

Печать значений макросов в читаемом виде

Условный пример, есть группы макросов (таких групп около 20 по проекту + неизвестное количество из винапи):
#define DTR_CONTROL_DISABLE 0x00 #define DTR_CONTROL_ENABLE 0x01 #define DTR_CONTROL_HANDSHAKE 0x02
Может быть существует какой-либо способ при выводе на печать (в лог, в консоль) получить именно имя макроса, чтобы в логе было что-то вроде:
DtrControl: [0х01] DTR_CONTROL_ENABLE
Текущая реализация: функции to_str, внутри у каждой из которых switch блок, вида:
switch (dcb.fDtrControl) { case DTR_CONTROL_DISABLE: tmp = "DTR_CONTROL_DISABLE"; break; case DTR_CONTROL_ENABLE: tmp = "DTR_CONTROL_ENABLE"; break; case DTR_CONTROL_HANDSHAKE: tmp = "DTR_CONTROL_HANDSHAKE"; break; default: tmp = QString("Unknown [%1]").arg(dcb.fDtrControl); }
Буду благодарен за любые советы как можно упростить, сделать понятнее, проще и читабельнее.


Ответ

Си - не мой основной язык, поэтому мой вариант может быть субоптимальным, но можно сделать нечто подобное на макросах:
#include
#define FOO_1 1 #define FOO_2 2
#define STR_(x) #x #define STR(x) STR_(x) #define FOO_STR(x) "[" STR(x) "] " #x
int main(void) { printf("%s
", FOO_STR(FOO_1)); printf("%s
", FOO_STR(FOO_2)); return 0; }
Вывод:
[1] FOO_1 [2] FOO_2

среда, 16 января 2019 г.

Раскрытие макросов

Есть маленький вопрос по раскрытиям макросов.
Например, этот код работает:
#define DEF_STRUCT(name, type) struct name {using t = type;}; DEF_STRUCT(hah, int)
И это:
#define DEF_STRUCT(name, type) struct name {using t = type;}; #define BUILD_STRUCT_1(name, type) DEF_STRUCT(name, type) BUILD_STRUCT_1(heh, char)
А вот этот ругается (Требуется спецификатор):
#define DEF_STRUCT(name, type) struct name {using t = type;}; #define BUILD_STRUCT_1(name, type) DEF_STRUCT(name, type) #define BUILD_STRUCT_2(name, type, ...) DEF_STRUCT(name, type) BUILD_STRUCT_1(__VA_ARGS__) BUILD_STRUCT_2(lol, bool, kek, unsigned) // Извини, не хочу работать
Вопрос: как он раскрывается и что следует поменять чтобы заработало как хотелось (Объявление 2-х структур подряд)?


Ответ

Код прекрасно компилируется GCC. В MSVC он не компилируется из-за нестандартного поведения препроцессора. Эту разницу в поведении препроцессора можно проиллюстрировать примером без __VA_ARGS__
#define M2(a, b) #define M1(x) M2(x) /* <- Считается ошибкой в MSVC */ #define A 1, 2 M1(A)
В качестве workaround можно предложить
#define BUILD_STRUCT_2(name, type, name2, ...) \ DEF_STRUCT(name, type) BUILD_STRUCT_1(name2, __VA_ARGS__)
или более общее
#define FIRST(x, ...) x #define TAIL(x, ...) __VA_ARGS__
#define BUILD_STRUCT_2(name, type, ...) \ DEF_STRUCT(name, type) BUILD_STRUCT_1(FIRST(__VA_ARGS__), TAIL(__VA_ARGS__))
Хотя не совсем понятно, зачем здесь ... и __VA_ARGS__ вообще.

пятница, 14 декабря 2018 г.

Автогенерация кода

Подскажите, пожалуйста, на каком из языков программирования можно написать программу, которая способна создать другую программу, а так же полностью изменить себя? Какие способы есть для этого, макросы, метапрограммирование? Чем тогда отличаются макросы ассемблера от макросов Lisp?


Ответ

Подскажите пожалуйста на каком из языков программирования можно написать программу которая способна создать другую программу, а так же полностью изменить себя? Для использования возможностей метапрограммирования достаточно наличия в языке eval. Возможность построить и выполнить произвольный код который будет влиять на текущий контекст — мощное средство с помощью которого можно писать программы, пишущие программы. Чтобы "изменять себя" достаточно переопределять существующий код в рантайме. Это можно сделать много где: Smalltalk, Python, Ruby, JavaScript. Иногда много средств для изменения кода на лету доступно из без eval, когда среда поддерживает рефлексию. (Smalltalk) Какие способы есть для этого, макросы, метапрограммирование? Чем тогда отличаются макросы ассемблера от макросов Lisp? Макросы Lisp — это просто удобное средство для преобразования AST перед компиляцией ничего больше. Разница в том, что макросы asm и C-предпроцессора оперируют кусками текста. Макросы Lisp оперируют выражениями. Кроме того, макросы в Lisp -- полноценные программы, и с помощью них можно строить весьма сложные выражения. UPD eval — это процедура которая принимает исходный код в виде данных (просто строку или AST) и выполняет его. Например в JavaScript можно написать так: eval("function some_func() { return 1; }") Здесь код передан как простой текст, но после выполнения будет объявлена функция some_func в текущем контексте, и мы сможем ею оперировать. some_func() // вернёт 1 Так как передаётся простой текст, то мы можем его преобразовывать любыми способами, и в итоге выполнить произвольный код в рантайме. AST — abstract syntax tree, такая структура данных (дерево) в которую преобразуется исходный код какого-либо языка после парсинга. После того как получено AST обычно над ним производят ряд преобразований (оптимизаций), а потом компилируют в машинный код. В случае Lisp из-за простоты синтаксиса AST будет обычный линейный список, например код: (some-construction (for i 0) ((i) exit)) В виде списка (AST) будет почти так же: (list 'some-construcion (list 'for 'i 0) (list (list 'i) 'exit)) ;; Или достаточно закавычить, чтобы получить тот же список '(some-construction (for i 0) ((i) exit)) Макросы — просто программы которые оперируют этой структурой данных, уже после того как код был прочтён, но ещё не оптимизирован и не скомпилирован. Они позволяют легко встраивать новые конструкции в язык, которые потом преобразуются в какие-то стандартные конструкции. Полезно взглянуть на язык Nemerle, который также поддерживает макросы, но не является лиспоподобным.

понедельник, 1 октября 2018 г.

Зачем нужна конструкция do { … } while(0)?

В библиотеке freeglut в файле freeglut_callbacks.c нашел такой код:
/* * All of the callbacks setting methods can be generalized to this: */ #define SET_CALLBACK(a) \ do \ { \ if( fgStructure.CurrentWindow == NULL ) \ return; \ SET_WCB( ( *( fgStructure.CurrentWindow ) ), a, callback ); \ } while( 0 )
Не понимаю, зачем здесь нужен цикл, ведь он в любом случае выполниться всего один раз, разве нет?


Ответ

Конструкция широко используется в "функциональных" макросах и предназначена для объединения нескольких statement и объявлений внутри макроса в один составной statement. Такое объединение нужно для того, чтобы макрос можно было использовать как обычный вызов функции в ветках условного оператора, циклах и т.п. без необходимости постоянно заключать этот вызов во внешнюю пару { ... }
На первый взгляд того же самого можно было бы достичь просто путем заключения самого определения макроса в пару { ... } вместо do { ... } while (0). Однако вариант с { ... } будет обладать одним неприятным недостатком - надо будет постоянно помнить, что его тело представляет из себя { ... }, а после { ... } в С не всегда можно ставить .
Например, если бы тело SET_CALLBACK было заключено просто в { ... }, то вот такой естественно выглядящий код не скомпилировался бы по причине того, что из-за поставленной после SET_CALLBACK(foo) точки с запятой ветка else оказалось оторванной от своего if
if (true) SET_CALLBACK(foo); // <- "Лишняя" `;` else // <- Ошибка: оторванный `else` /* что-то еще */;
Вот тут-то на сцену и выходит do { ... } while (0). Цикл do/while уникален тем, что является единственной (почти) в языке С грамматической конструкцией, которая формирует блок, и при этом всегда безусловно заканчивается на . Благодаря этой замыкающей мы можем более естественным образом использовать do { ... } while (0) внутри составных макросов для объединения их в единый statement. В вышеприведенном примере с if , указанная после SET_CALLBACK(foo), будет "поглощена" конструкцией do { ... } while (0) и не приведет в "разрушению" целостности if
Вот именно ради того, чтобы иметь возможность "вызывать" функциональные макросы через тот же естественный синтаксис, через который мы вызываем обычные функции, тело составного макроса и заключают не в { ... }, а именно в do { ... } while (0)
P.S. Почему авторы языка в свое время решили настоять на включении [теоретически совсем не нужной] замыкающей в синтаксис do/while - не совсем ясно, но это было сделано.