Страницы

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

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

понедельник, 30 марта 2020 г.

Посмотреть код без инструкций препроцессора

#cpp #c #препроцессор


Есть ли программа, которая бы как компилятор начинала собирать проект и останавливалась
после этапа препроцессора? Чтобы оставался чистый код, как если бы его писали без препроцессорных
команд. Хотя было бы неплохо, чтобы в итоге он бы разделял фрагменты кода по именам
заголовочных файлов. 
    


Ответы

Ответ 1



Как минимум в gcc (и, соответственно, в его производных, типа minGW) препроцессор — это отдельная программа, которая назвается cpp: cpp -o yourfile.c.preprocessed yourfile.c Поверьте, один раз прочитав его вывод, вы поймёте, почему фраза "бойтесь своих желаний, они могут сбыться" не лишена смысла :)

Ответ 2



До кучи :) - в Visual C++ ключ /P создаст файл с тем же именем и расширением .i (можно изменить с помощью ключа /Fi), в который будут включены имена включаемых файлов и номера строк. В OpenWatcom -pl выведет информацию в stdout, для вывода в файл надо либо перенаправить вывод, либо указать имя с помощью ключа -fo.

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

Как передать “text” в C используя Makefile

#c #makefile #препроцессор


Есть фаил BoardConfig.mk
В нем есть команда

TARGET_BOARD_PLATFORM := mt6572


Мы берём это значение в Android.mk так

init_options += -Dold_kernel=$(TARGET_BOARD_PLATFORM)


и передаем его в init.cpp

{ "ro.boot.hardware",   "ro.hardware",   old_kernel, },


В итоге мы имеем:

{ "ro.boot.hardware",   "ro.hardware",   mt6572, },


Как сделать так, что бы передаваемое значение заключалась в "" ?
То есть что бы получалась такая строка:

{ "ro.boot.hardware",   "ro.hardware",   "mt6572", },

    


Ответы

Ответ 1



Надо просто в Android.mk изменить строку на: init_options += -Dold_kernel=\"$(TARGET_BOARD_PLATFORM)\"

Ответ 2



В .cpp-файле добавляем макроопределение типа #define STR(x) #x и пишем {"ro.boot.hardware", "ro.hardware", STR(old_kernel), }, Все.

среда, 5 февраля 2020 г.

Упростить написание кода макросом

#cpp #препроцессор


Имеется множество примерно таких методов:

void libvlc::Interop::LibVLCHelper::libvlc_media_player_set_media(libvlc_media_player_t
* p_mp, libvlc_media_t * p_md)
{
    if (!m_vlc_media_player_set_media)
    {
        m_vlc_media_player_set_media = m_libvlc_->Resolve(__func__);
    }
    (*m_vlc_media_player_set_media)(p_mp, p_md);
}


Хочу сделать примерно такой макрос:

#define MF(t, x, name) if (!(x)) { (x) = m_libvlc_->Resolve(name); }


И определение на валидность упрощается соответственно до такого вида:

libvlc_media_player_t * libvlc::Interop::LibVLCHelper::libvlc_media_player_new(libvlc_instance_t
* p_instance)
{
    MF(vlc_media_player_new, m_vlc_media_player_new, __func__);
    return (*m_vlc_media_player_new)(p_instance);
}


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

libvlc_media_player_t * libvlc::Interop::LibVLCHelper::libvlc_media_player_new(libvlc_instance_t
* p_instance)
{
    MF(vlc_media_player_new, __func__);
    return (*m_vlc_media_player_new)(p_instance);
}


Для понятия: Макрос принимает тип, 1м параметром, подставляет m_(тут тип), и вид
примерно такой:

#define MF(t, name) if (!(m_#t)) { (m_#t) = m_libvlc_->Resolve(name); }



  Не очень хорошо дружу с макросами, и не могу понять как такое
  реализовать.
  
  Примечание: Имя поля всегда отличается от типа только добавочным m_

    


Ответы

Ответ 1



Ваш код #define MF(t, name) if (!(m_#t)) { (m_#t) = m_libvlc_->Resolve(name); } почти правильный. Для token pasting («склеивания» токенов) нужно использовать удвоенный символ диеза. #define MF(t, name) if (!(m_##t)) { (m_##t) = m_libvlc_->Resolve(name); } (Практически одновременно тот же совет в комментарии дал @VTT.)

Ответ 2



Долой макросы! Даёшь шаблоны и вывод типов! Предположу, что можно упростить код до следующего. class /* имя класса, отвечающего переменной m_libvlc_ */ { ... template T& SafeGet(T*& member, const char* func) { if (!member) { member = Resolve(func); } return *member; } } Использование: void libvlc::Interop::LibVLCHelper::libvlc_media_player_set_media(libvlc_media_player_t * p_mp, libvlc_media_t * p_md) { m_libvlc_->SafeGet(m_vlc_media_player_set_media, __func__)(p_mp, p_md); } Ну и если всё-таки хочется макрос.. #define SAFE_GET(member) m_libvlc_->SafeGet(member, __func__) и void libvlc::Interop::LibVLCHelper::libvlc_media_player_set_media(libvlc_media_player_t * p_mp, libvlc_media_t * p_md) { SAFE_GET(m_vlc_media_player_set_media)(p_mp, p_md); } P.S. Можно добавить в этот вызов и аргументы через class ...Args в шаблоне, но такой вариант показался приятнее.

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

Директивы препроцессора #ifdef #define и #undef

#cpp #препроцессор


В чем здесь проблема? Почему n всегда равно либо 100 и 100, либо 200 и 200, как ни крути.

#include 
#include 

using namespace std;

#ifdef A
    #define N 100
#else  
    #define N 200
#endif


int main()
{
    #define A
    int n = N;
    cout << n << endl;
    #undef A
    #define B
    n = N;
    cout << n << endl;
    #undef B
    return 0;
}

    


Ответы

Ответ 1



Предпроцессор - нужен не для того, что бы писать программу времени выполнения, а для того, что бы сгенерировать код(программу) времени компиляции. Например создать две сборки debug и release. Или в вашем случае два модуля, один с N=100 и второй N=200. Всё верно. Или так или так, но не две одновременно (условно). Предпроцессор не умеет делать "процедуры и функции" процессинга в широком смысле (функции аргумента - существуют). Т.е. реальный код генерируется за один проход. Если один раз сделано #define N 100 то N не изменит значение до тех пор пока снова не встретится define N или undef N (зачёркиваем "условно"). Другого способа повлиять - нету. В коде все директивы выполняются последовательно. Т.е. код #ifdef A #define N 100 #else #define N 200 #endif int main() { #define A int n = N; cout << n << endl; #undef A #define B n = N; cout << n << endl; #undef B return 0; } Является "виртуальным" в реальности предпроцессор делает из него такой код // N = 200 int main() { // A задано, N без изменений int n = 200; cout << n << endl; // B задано, А не_задано, N без изменений n = 200; cout << n << endl; return 0; } Но на самом деле всё "условно", что бы ваш код работал, вам его нужно поправить так вставив фрагмент ниже, или сохранить это фрагмент в файл например def_A.h c таким кодом #ifdef A #define N 100 #else #define N 200 #endif Условно, потому что есть способ заставить перегенерировать директивы. Просто нужно сказать предпроцессору - хочу перезадать N вот так например int main() { #define A #include "def_A.h" //#ifdef A // #define N 100 //#else // #define N 200 //#endif int n = N; cout << n << endl; #undef A #define B #include "def_A.h" //#ifdef A // #define N 100 //#else // #define N 200 //#endif n = N; cout << n << endl; #undef B return 0; } Вставил комментраием то, что делает директива include. Приводить какой код сгенерирует предпроцессор не буду, думаю это очевидно. И как последний штрих, допишите перед #define N 100, а лучше перед #ifdef A директиву #undef N. Это не обязательно, это уберет warning или hint с уведомлением что "переопределение N не совпадает с предыдущим предопределением".

Ответ 2



Макроопределения не меняются по ходу действия. Идет обработка препроцессором. Доходит дело до блока #ifdef A #define N 100 #else #define N 200 #endif Препроцессор соображает: определено ли A? Нет? Отлично, так и запишем, что далее N меняется на 200. И дальше, когда дело доходит до n = N;, это N заменяется на уже записанное в своих каких-то внутренних таблицах препроцессора 200 независимо от других макроопределений.

Ответ 3



В качестве иллюстрации к ответу от Harry. Возьмем простую программу, полностью аналог вашей, только вместо препроцессора - процессор: int N; bool b = true; if (b) N = 100; else N = 200; b = false; cout << N; Что будет напечатано? По вашей логике - 200, но напечатается, естественно, 100. Как и процессор, препроцессор С просматривает программу последовательно, вычисляя выражения по мере просмотра. Если вы изменили значение переменной, это оказывает влияние только на последующие операции.

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

Конвертация SCSS в SASS

#вёрстка #sass #scss #map #препроцессор


Большой проект, написан на SASS и использованием синтаксиса SASS, а не SCSS. Поэтому
изменить файл на SCSS не вариант, так как их очень много. 

Нужно использовать ассоциативные массив(map) подобного вида:

$primary-colors: (
  "red":   "#ff0000",
  "green": "#00ff00",
  "blue":  "#0000ff"
);


Это синтаксис SCSS, а мне нужен SASS. На конструкцию такого вида компилятор ругается:


  Error: unclosed parenthesis
  
  on line 394 of style/_intro-val.sass
  
  $primary-colors: ( {    ----------------^


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


Ответы

Ответ 1



Нашла решение! Синтаксис SASS не поддерживает многострочность, это конечно портит простоту отладки кода и наглядность, однако проблема была решена вот так: $primary-colors: ("red": "#ff0000", "green": "#00ff00", "blue": "#0000ff"); Возможно кому-то пригодится.

вторник, 31 декабря 2019 г.

Макрос, который зависит от аргументов

#cpp #c #препроцессор


Хочется макрос, который расширяется в разное в зависимости от наличия или отсутствия
у него аргументов. Пример:

_return();     /* расширяется в return(0xDEAFBEEF); */
_return(a+b);  /* расширяется в return(a+b);        */


Как?

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


Ответы

Ответ 1



Немного поправил исходный комплект макросов по ссылке. Вот что получилось: #define __VA_NARG__(...) \ __VA_NARG_(_0, ## __VA_ARGS__, __RSEQ_N()) #define __VA_NARG_(...) \ __VA_ARG_N(__VA_ARGS__) #define __VA_ARG_N( \ _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ _61,_62,_63,N,...) N #define __RSEQ_N() \ 62, 61, 60, \ 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 #define JOIN(x,y) _JOIN(x,y) #define _JOIN(x,y) x##y #define r(...) JOIN(r,__VA_NARG__(__VA_ARGS__))(__VA_ARGS__) #define r0() nullargs() #define r1(x) oneargs(x) r(x); r(); Тестирую на cpp из gcc. Получил в результате всех подстановок: oneargs(x); nullargs(); Двойная передача JOIN->_JOIN->## - иначе не разворачиваются макроаргументы. Операция препроцессора ## склеивает без пробелов и получается новый идентификатор. В макросах с переменным числом аргументов ## после запятой и перед __VA_ARGS__ убирает запятую, если аргументов нет - это расширение gcc, поэтому в других компиляторах может не работать.

пятница, 27 декабря 2019 г.

Си. Условные операторы в макроопределении препроцессора. Возможно ли?

#c #препроцессор


Нужно исключить отладочные printf() из программы, применяя константу препроцессора.
Обрамлять каждый вызов printf()

#if DEBUG == 1
printf();
#endif


не хочется. Можно ли сделать что-то типа:

#define printf(x,y) (if DEBUG == 1 printf(x,y) endif)


#if внутри макроопределений запрещены. Есть ли альтернатива?
    


Ответы

Ответ 1



Можно определять макрос по разному в зависимости от значения DEBUG: #if DEBUG==1 #define printf_d(...) printf(__VA_ARGS__); #else #define printf_d(...) #endif Но лучше сделать это немного по другому, проверяя не значение, а факт наличия макроопределения: #ifdef DEBUG // . . . Такой подход позволяет включить режим отладки простым указанием имени проверяемого макроса в Make-файле или даже в параметрах команды сборки.

Ответ 2



Объявите разные версии макроса PRINT - для отладки и обычную #ifdef _DEBUG #define PRINT(x,y) print(x,y) #else #define PRINT(x,y) #endif

Ответ 3



Вот прям не знаю даже, какому из 2-х вариантов (@kff или @gbg) отдать предпочтение: в обоих что-то да не нравится, вот такой нравится: #ifdef _DEBUG #define PRINT(...) print(__VA_ARGS__) #else #define PRINT(...) #endif его и использую, а вот вместо _DEBUG тут лучше использовать любой другой флаг, чтобы отладочную печать можно было включить, независимо от глобальной оптимизации. На самом деле, я часто использую немного другую схему: у меня есть разные версии PRINT, с разными префиксами, чтобы можно было логику вывода на печать детализировать на разных уровнях: #ifdef ENABLE_PRINT #define PRINT(...) print(__VA_ARGS__) #else #define PRINT(...) #endif #ifdef ENABLE_VERB_PRINT #define VERB_PRINT(...) print(__VA_ARGS__) #else #define VERB_PRINT(...) #endif ... А в коде - уже расставляем версии PRINT'ов, в зависимости от логики работы функций: void myFunc(){ VERB_PRINT("func: %s", __FUNCTION__); ... if( !... ){ PRINT("error:%...", ...); } ... } Теперь, можно не меняя код, управлять детализацией логирования, добавляя соответствующие флаги для препроцессора.

понедельник, 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

среда, 11 декабря 2019 г.

Автогенерация классов в C++

#cpp #препроцессор


Имеется структура данных, подобная следующей:

Group_1
    Type_1_1 Variable_1_1
    . . .
    Type_1_N Variable_1_N
    Group_1_1 Name_1_1
        Type_1_1_1  Variable_1_1_1
        . . .
        Type_1_1_N1 Variable_1_1_N1
    . . .
    Group_1_M Name_1_M
        Type_1_M_1  Variable_1_M_1
        . . .
        Type_1_M_N2 Variable_1_M_N2


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

Для указанной структуры необходимо построить C++-код следующего вида:

class Group_1_1 {
    public :
        Type_1_1_1  Variable_1_1_1;
        // . . .
        Type_1_1_N1 Variable_1_1_N1;
        void f(Worker& worker) {
            worker.begin("Group_1_1");
            worker.work("Variable_1_1_1",  Variable_1_1_1);
            // . . .
            worker.work("Variable_1_1_N1", Variable_1_1_N1);
            worker.end();
        }
}
// . . .

class Group_1 {
    public :
        Type_1_1 Variable_1_1;
        // . . .
        Type_1_N Variable_1_N;
        Group_1_1 Name_1_1;
        // . . .
        Group_1_M Name_1_M;
        void f(Worker& worker) {
            worker.begin("Group_1");
            worker.work("Variable_1_1",  Variable_1_1_1);
            // . . .
            worker.work("Variable_1_N", Variable_1_1_N1);
            Name_1_1.f(worker);
            // . . .
            Name_1_M.f(worker);
            worker.end();
        }
}


Здесь Worker - некоторый внешний класс.

Построить код необходимо без использования каких-либо внешних инструментов, можно
использовать только стандартные возможности компилятора и препроцессора. То есть можно
использовать макросы и шаблоны, но нельзя запускать какие-либо программы, не входящие
в состав компилятора. Можно при этом ограничиться стандартом c++11, совместимость с
предыдущими стандартами не требуется. Решение должно работать по меньшей мере со следующими
компиляторами: GCC 4.9+ (Linux), MinGW 4.9+ (Windows), MSVC 2015.

Собственно вопрос: можно ли это реализовать и если можно, то как?
    


Ответы

Ответ 1



Предлагаю решение с ипользованием boost::preprocessor. Пока что это решение только половины задачи, генерации отдельных классов. Входные данные должны быть представлены примерно так: #define GEN_GROUP 1 #define GEN_VAR 0 #define MY_STRUCT \ (GEN_GROUP, AType, A, (GEN_VAR, int, AInt) \ , (GEN_GROUP, BType, B, (GEN_VAR, int, BInt) \ , (GEN_VAR, float, BFloat) \ ) \ , (GEN_VAR, char, AChar) \ ) В понятиях boost::preprocessor это кортежи (tuple). А точнее кортежи которые содержат другие кортежи и таким образом образуют дерево. Струтура это дерева такая: элемент 0 - тип узла (группа или переменная), элемент 1 - тип данных, 2 - имя узла, и в случае группы остальные элементы начиная с 3 это поля или подгруппы: группа = (1, тип, имя, [переменная | группа]+ ) переменная = (0, тип, имя) Использовать эту структуру надо следующим образом, приведу просто код с коментариями: #include #define GEN_NODE_TYPE(node) BOOST_PP_TUPLE_ELEM(0, node) #define GEN_TYPE(node) BOOST_PP_TUPLE_ELEM(1, node) #define GEN_NAME(node) BOOST_PP_TUPLE_ELEM(2, node) #define GEN_CHILDS(node) \ // конвертируем в список, выкусываем хвост начиная с элемента 3 BOOST_PP_LIST_REST_N(3, BOOST_PP_TUPLE_TO_LIST(node)) // функтор для генерации полей класа вида "тип имя;" #define GENERATE_MEMBER_PRED(r, data, member) \ GEN_TYPE(member) GEN_NAME(member); // функтор для генерации вызовов worker внутри фнукции f #define GENERATE_WORK_CALL_PRED(r, data, member) \ BOOST_PP_IF(GEN_NODE_TYPE(member) \ // если тип "группа" , GEN_NAME(member).f(worker) \ // то "имя.f(worker)" , worker.work(BOOST_PP_STRINGAZE(GEN_NAME(member)) \ // заковычиваем имя ,GEN_NAME(2, member)) \ // иначе worker.work("имя", имя) ); // точка с запятой для всех #define GENERATE_CLASS(node) \ class GEN_TYPE(node) \ // "class тип" { \ public: \ \ // генерируем поля BOOST_PP_LIST_FOR_EACH( \ GENERATE_MEMBER_PRED \ // выполняем для каждого элемента , _ \ // здесь можно пусто , GEN_CHILDS(node) \ // список детей ) \ \ void f(Worker & worker) \ { \ worker.begin(BOOST_PP_STRINGAZE(GEN_TYPE(member))); \ // заковычивам тип \ \ // генерируем вызовы worker для полей BOOST_PP_LIST_FOR_EACH( \ GENERATE_WORK_CALL_PRED \ // выполняем для каждого ребенка , _ \ // здесь можно пусто , GEN_CHILDS(node) \ // список детей ) \ \ worker.end(); \ } \ }; Результат для GENERATE_CLASS(MY_STRUCT): class AType { public: int AInt; BType B; char AChar; void f(Worker & worker) { worker.begin("AType"); worker.work("AInt", AInt); B.f(worker); worker.work("AChar", AChar); worker.end(); } }; Как видите, классы для вложенных групп не генерируются, но это уже хоть что-то. Чтобы сделать вложенные группы необходимо реализовать обход входного дерева, при котором для каждого узла, если он группа, вызывать GENERATE_CLASS. Так как препроцессор не поддерживает рекурсию макросов, обход надо делать нерекурсивный со стеком. На boost::preprocessor по идее это возможно, но сложно, предлагаю это для самостоятельной проработки. Внимание! Самое большое ограничение такого подхода в том, что бесконечную структуру обработать не получится. Так как количество итераций в любом циклическом операторе boost::preprocessor ограничено, равно как и в любом другом подобном инструменте основанном на препроцессоре Си. Сейчас, например, максимальное количество итераций для FOR_* составляет 256. Поэтому для больших циклов вам нужно править библиотеку boost::preprocessor либо делать свой клон с каким вам надо количеством итераций.

суббота, 30 ноября 2019 г.

Знак ## в языке Си

#c #препроцессор


При просмотре алгоритма BlowFish нашел вот такую строчку:

#define S(x,i) (SBoxes[i][x.w.byte##i])


Что означает ##?
    


Ответы

Ответ 1



Это соединение строк. В данном случае значение параметра макроса "приклеивается" к byte Допустим, макрос вызвали с параметрами var и 1, то S(var, 1) будет заменено препроцессором на (SBoxes[1][var.w.byte1]) а вызов S(var, str) на (SBoxes[str][var.w.bytestr]) и так далее

Ответ 2



Это token pasting, слияние токенов на уровне препроцессора. Например, S(field,5) раскроется в (SBoxes[5][field.w.byte5]). Документация: MSDN GNU GCC cppreference

среда, 27 ноября 2019 г.

Как собрать include из define

#c++ #c #препроцессор


Как из 

#define filename myfilename
#define path ../driver/


перейти к

#include INCLUDE_DRIVER;


которое после препроцессинга перейдет в

#include "../driver/myfilename.h"


Я потратил уже немало времени, на различные комбинации ##, но так и не пришел к тому
что нужно. Я бы продолжил эксперементы, если бы был хорошо виден результат. 
    


Ответы

Ответ 1



#define path ../driver/ А кавычки кто ставить будет? http://codepad.org/vi8lh5nI Там два дефайна определяют путь в условии. Разобрался. Вот работающая последовательность шагов: http://codepad.org/rGostnci http://codepad.org/wCx3Of1Y http://codepad.org/VpQ3O1AJ Итоговый вариант кода: #define STD std #define IOH io.h #define STR(x) #x #define JOIN(x,y) STR(x##y) #define INC(x,y) JOIN(x,y) #include INC(STD,IOH) int main(void) { return !printf("YES"); } Почему так? Объединение "таких" "строк" делается потом. Include не должен содержать такого. Поэтому разворачивание аргумента в строку (единственного аргумента) должно быть сделано на последнем шаге. Это делает STR. Для передачи аргументов в STR их надо объединить - это делает JOIN. Поскольку include передаёт то, что само по себе define, если сразу вызвать JOIN, то объединятся имена дефайнов, а не значения. Добавляем ещё один уровень, чтобы они развернулись - это INC. Собственно всё, оно работает. А теперь меняем std и io.h на данные ТС, запускаем в gcc и получаем кучу ругани Проверил: http://codepad.org/WY0TkYMS Line 0: error: pasting "/" and "myfilename" does not give a valid preprocessing token Line 27: error: ../driver/myfilename: No such file or directory Возможно, с этим можно сделать что-то при помощи ключей компиляции. Потому что есть какая-то мутная ошибка про слеш, но дальше в 27й строке делается попытка подключить файл с правильно сформированным именем (естественно, на codepad'e его нет). К сожалению, я не знаток ключей компиляции gcc. Возможно, кто-то сможет подсказать, как исправить эту проблему. Как насчёт использования фиктивного каталога, из которого потом поднимемся вверх?http://codepad.org/OCpT6U2BСпасибо avp за идею. #define filename _/../myfilename #define path ../driver/_ #define STR(x) #x #define JOIN(x,y) STR(x##y) #define INC(x,y) JOIN(x,y) #include INC(path,filename) int main(void) { return !printf("YES"); }

Ответ 2



Похоже, в gcc это не решается (по крайней мере корректно). Мне удалось "сделать" только подавив всю диагностику через > gcc -E c1.c /tmp/t.c 2>/dev/null; gcc /tmp/t.c. / Вообще в документации по поводу конкатенации пишут: However, two tokens that don't together form a valid token cannot be pasted together. For example, you cannot concatenate x with + in either order. If you try, the preprocessor issues a warning and emits the two tokens. Whether it puts white space between the tokens is undefined. – avp 25 май в 13:26

пятница, 7 июня 2019 г.

Посмотреть код без инструкций препроцессора

Есть ли программа, которая бы как компилятор начинала собирать проект и останавливалась после этапа препроцессора? Чтобы оставался чистый код, как если бы его писали без препроцессорных команд. Хотя было бы неплохо, чтобы в итоге он бы разделял фрагменты кода по именам заголовочных файлов.


Ответ

Как минимум в gcc (и, соответственно, в его производных, типа minGW) препроцессор — это отдельная программа, которая назвается cpp:
cpp -o yourfile.c.preprocessed yourfile.c
Поверьте, один раз прочитав его вывод, вы поймёте, почему фраза "бойтесь своих желаний, они могут сбыться" не лишена смысла :)

пятница, 19 апреля 2019 г.

Как передать “text” в C используя Makefile

Есть фаил BoardConfig.mk В нем есть команда
TARGET_BOARD_PLATFORM := mt6572
Мы берём это значение в Android.mk так
init_options += -Dold_kernel=$(TARGET_BOARD_PLATFORM)
и передаем его в init.cpp
{ "ro.boot.hardware", "ro.hardware", old_kernel, },
В итоге мы имеем:
{ "ro.boot.hardware", "ro.hardware", mt6572, },
Как сделать так, что бы передаваемое значение заключалась в "" ? То есть что бы получалась такая строка:
{ "ro.boot.hardware", "ro.hardware", "mt6572", },


Ответ

Надо просто в Android.mk изменить строку на:
init_options += -Dold_kernel=\"$(TARGET_BOARD_PLATFORM)\"

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

Упростить написание кода макросом

Имеется множество примерно таких методов:
void libvlc::Interop::LibVLCHelper::libvlc_media_player_set_media(libvlc_media_player_t * p_mp, libvlc_media_t * p_md) { if (!m_vlc_media_player_set_media) { m_vlc_media_player_set_media = m_libvlc_->Resolve(__func__); } (*m_vlc_media_player_set_media)(p_mp, p_md); }
Хочу сделать примерно такой макрос:
#define MF(t, x, name) if (!(x)) { (x) = m_libvlc_->Resolve(name); }
И определение на валидность упрощается соответственно до такого вида:
libvlc_media_player_t * libvlc::Interop::LibVLCHelper::libvlc_media_player_new(libvlc_instance_t * p_instance) { MF(vlc_media_player_new, m_vlc_media_player_new, __func__); return (*m_vlc_media_player_new)(p_instance); }
Да, он работает, но можно ли улучшить макрос до примерно такого вызова?
libvlc_media_player_t * libvlc::Interop::LibVLCHelper::libvlc_media_player_new(libvlc_instance_t * p_instance) { MF(vlc_media_player_new, __func__); return (*m_vlc_media_player_new)(p_instance); }
Для понятия: Макрос принимает тип, 1м параметром, подставляет m_(тут тип), и вид примерно такой:
#define MF(t, name) if (!(m_#t)) { (m_#t) = m_libvlc_->Resolve(name); }
Не очень хорошо дружу с макросами, и не могу понять как такое реализовать. Примечание: Имя поля всегда отличается от типа только добавочным m_


Ответ

Ваш код
#define MF(t, name) if (!(m_#t)) { (m_#t) = m_libvlc_->Resolve(name); }
почти правильный. Для token pasting («склеивания» токенов) нужно использовать удвоенный символ диеза.
#define MF(t, name) if (!(m_##t)) { (m_##t) = m_libvlc_->Resolve(name); }
(Практически одновременно тот же совет в комментарии дал @VTT.)

вторник, 5 марта 2019 г.

Конвертация SCSS в SASS

Большой проект, написан на SASS и использованием синтаксиса SASS, а не SCSS. Поэтому изменить файл на SCSS не вариант, так как их очень много.
Нужно использовать ассоциативные массив(map) подобного вида:
$primary-colors: ( "red": "#ff0000", "green": "#00ff00", "blue": "#0000ff" );
Это синтаксис SCSS, а мне нужен SASS. На конструкцию такого вида компилятор ругается:
Error: unclosed parenthesis on line 394 of style/_intro-val.sass $primary-colors: ( { ----------------^
Как исправить написание? Кто-нибудь сталкивался с такой проблемой? Может быть заменить скобку на какой-то другой символ или отступ определенный должен быть?


Ответ

Нашла решение! Синтаксис SASS не поддерживает многострочность, это конечно портит простоту отладки кода и наглядность, однако проблема была решена вот так:
$primary-colors: ("red": "#ff0000", "green": "#00ff00", "blue": "#0000ff");
Возможно кому-то пригодится.

вторник, 25 декабря 2018 г.

Макрос, который зависит от аргументов

Хочется макрос, который расширяется в разное в зависимости от наличия или отсутствия у него аргументов. Пример:
_return(); /* расширяется в return(0xDEAFBEEF); */ _return(a+b); /* расширяется в return(a+b); */
Как?
В принципе набрёл на такую ветку, но дальше что? Ну узнали мы сколько аргументов, и...


Ответ

Немного поправил исходный комплект макросов по ссылке. Вот что получилось:
#define __VA_NARG__(...) \ __VA_NARG_(_0, ## __VA_ARGS__, __RSEQ_N()) #define __VA_NARG_(...) \ __VA_ARG_N(__VA_ARGS__) #define __VA_ARG_N( \ _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ _61,_62,_63,N,...) N #define __RSEQ_N() \ 62, 61, 60, \ 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define JOIN(x,y) _JOIN(x,y) #define _JOIN(x,y) x##y
#define r(...) JOIN(r,__VA_NARG__(__VA_ARGS__))(__VA_ARGS__) #define r0() nullargs() #define r1(x) oneargs(x)
r(x); r();
Тестирую на cpp из gcc. Получил в результате всех подстановок:
oneargs(x); nullargs();
Двойная передача JOIN->_JOIN->## - иначе не разворачиваются макроаргументы.
Операция препроцессора ## склеивает без пробелов и получается новый идентификатор. В макросах с переменным числом аргументов ## после запятой и перед __VA_ARGS__ убирает запятую, если аргументов нет - это расширение gcc, поэтому в других компиляторах может не работать.

четверг, 6 декабря 2018 г.

Си. Условные операторы в макроопределении препроцессора. Возможно ли?

Нужно исключить отладочные printf() из программы, применяя константу препроцессора. Обрамлять каждый вызов printf()
#if DEBUG == 1 printf(); #endif
не хочется. Можно ли сделать что-то типа:
#define printf(x,y) (if DEBUG == 1 printf(x,y) endif)
#if внутри макроопределений запрещены. Есть ли альтернатива?


Ответ

Можно определять макрос по разному в зависимости от значения DEBUG
#if DEBUG==1 #define printf_d(...) printf(__VA_ARGS__); #else #define printf_d(...) #endif
Но лучше сделать это немного по другому, проверяя не значение, а факт наличия макроопределения:
#ifdef DEBUG // . . .
Такой подход позволяет включить режим отладки простым указанием имени проверяемого макроса в Make-файле или даже в параметрах команды сборки.

вторник, 16 октября 2018 г.

Автогенерация классов в C++

Имеется структура данных, подобная следующей:
Group_1 Type_1_1 Variable_1_1 . . . Type_1_N Variable_1_N Group_1_1 Name_1_1 Type_1_1_1 Variable_1_1_1 . . . Type_1_1_N1 Variable_1_1_N1 . . . Group_1_M Name_1_M Type_1_M_1 Variable_1_M_1 . . . Type_1_M_N2 Variable_1_M_N2
Важно, что число групп и число уровней вложения может быть произвольным. Внутри каждой группы могут быть как другие группы, так и переменные. В общем случае на типы переменных никакие ограничения не накладываются.
Для указанной структуры необходимо построить C++-код следующего вида:
class Group_1_1 { public : Type_1_1_1 Variable_1_1_1; // . . . Type_1_1_N1 Variable_1_1_N1; void f(Worker& worker) { worker.begin("Group_1_1"); worker.work("Variable_1_1_1", Variable_1_1_1); // . . . worker.work("Variable_1_1_N1", Variable_1_1_N1); worker.end(); } } // . . .
class Group_1 { public : Type_1_1 Variable_1_1; // . . . Type_1_N Variable_1_N; Group_1_1 Name_1_1; // . . . Group_1_M Name_1_M; void f(Worker& worker) { worker.begin("Group_1"); worker.work("Variable_1_1", Variable_1_1_1); // . . . worker.work("Variable_1_N", Variable_1_1_N1); Name_1_1.f(worker); // . . . Name_1_M.f(worker); worker.end(); } }
Здесь Worker - некоторый внешний класс.
Построить код необходимо без использования каких-либо внешних инструментов, можно использовать только стандартные возможности компилятора и препроцессора. То есть можно использовать макросы и шаблоны, но нельзя запускать какие-либо программы, не входящие в состав компилятора. Можно при этом ограничиться стандартом c++11, совместимость с предыдущими стандартами не требуется. Решение должно работать по меньшей мере со следующими компиляторами: GCC 4.9+ (Linux), MinGW 4.9+ (Windows), MSVC 2015.
Собственно вопрос: можно ли это реализовать и если можно, то как?


Ответ

Предлагаю решение с ипользованием boost::preprocessor. Пока что это решение только половины задачи, генерации отдельных классов.
Входные данные должны быть представлены примерно так:
#define GEN_GROUP 1 #define GEN_VAR 0
#define MY_STRUCT \ (GEN_GROUP, AType, A, (GEN_VAR, int, AInt) \ , (GEN_GROUP, BType, B, (GEN_VAR, int, BInt) \ , (GEN_VAR, float, BFloat) \ ) \ , (GEN_VAR, char, AChar) \ )
В понятиях boost::preprocessor это кортежи (tuple). А точнее кортежи которые содержат другие кортежи и таким образом образуют дерево. Струтура это дерева такая: элемент 0 - тип узла (группа или переменная), элемент 1 - тип данных, 2 - имя узла, и в случае группы остальные элементы начиная с 3 это поля или подгруппы:
группа = (1, тип, имя, [переменная | группа]+ ) переменная = (0, тип, имя)
Использовать эту структуру надо следующим образом, приведу просто код с коментариями:
#include
#define GEN_NODE_TYPE(node) BOOST_PP_TUPLE_ELEM(0, node) #define GEN_TYPE(node) BOOST_PP_TUPLE_ELEM(1, node) #define GEN_NAME(node) BOOST_PP_TUPLE_ELEM(2, node) #define GEN_CHILDS(node) \ // конвертируем в список, выкусываем хвост начиная с элемента 3 BOOST_PP_LIST_REST_N(3, BOOST_PP_TUPLE_TO_LIST(node))
// функтор для генерации полей класа вида "тип имя;" #define GENERATE_MEMBER_PRED(r, data, member) \ GEN_TYPE(member) GEN_NAME(member);
// функтор для генерации вызовов worker внутри фнукции f #define GENERATE_WORK_CALL_PRED(r, data, member) \ BOOST_PP_IF(GEN_NODE_TYPE(member) \ // если тип "группа" , GEN_NAME(member).f(worker) \ // то "имя.f(worker)" , worker.work(BOOST_PP_STRINGAZE(GEN_NAME(member)) \ // заковычиваем имя ,GEN_NAME(2, member)) \ // иначе worker.work("имя", имя) ); // точка с запятой для всех
#define GENERATE_CLASS(node) \ class GEN_TYPE(node) \ // "class тип" { \ public: \ \ // генерируем поля BOOST_PP_LIST_FOR_EACH( \ GENERATE_MEMBER_PRED \ // выполняем для каждого элемента , _ \ // здесь можно пусто , GEN_CHILDS(node) \ // список детей ) \ \ void f(Worker & worker) \ { \ worker.begin(BOOST_PP_STRINGAZE(GEN_TYPE(member))); \ // заковычивам тип \ \ // генерируем вызовы worker для полей BOOST_PP_LIST_FOR_EACH( \ GENERATE_WORK_CALL_PRED \ // выполняем для каждого ребенка , _ \ // здесь можно пусто , GEN_CHILDS(node) \ // список детей ) \ \ worker.end(); \ } \ };
Результат для GENERATE_CLASS(MY_STRUCT)
class AType { public: int AInt; BType B; char AChar;
void f(Worker & worker) { worker.begin("AType"); worker.work("AInt", AInt); B.f(worker); worker.work("AChar", AChar); worker.end(); } };
Как видите, классы для вложенных групп не генерируются, но это уже хоть что-то. Чтобы сделать вложенные группы необходимо реализовать обход входного дерева, при котором для каждого узла, если он группа, вызывать GENERATE_CLASS. Так как препроцессор не поддерживает рекурсию макросов, обход надо делать нерекурсивный со стеком. На boost::preprocessor по идее это возможно, но сложно, предлагаю это для самостоятельной проработки.
Внимание! Самое большое ограничение такого подхода в том, что бесконечную структуру обработать не получится. Так как количество итераций в любом циклическом операторе boost::preprocessor ограничено, равно как и в любом другом подобном инструменте основанном на препроцессоре Си. Сейчас, например, максимальное количество итераций для FOR_* составляет 256. Поэтому для больших циклов вам нужно править библиотеку boost::preprocessor либо делать свой клон с каким вам надо количеством итераций.

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

Знак ## в языке Си

При просмотре алгоритма BlowFish нашел вот такую строчку:
#define S(x,i) (SBoxes[i][x.w.byte##i])
Что означает ##?


Ответ

Это соединение строк.
В данном случае значение параметра макроса "приклеивается" к byte
Допустим, макрос вызвали с параметрами var и 1, то
S(var, 1)
будет заменено препроцессором на
(SBoxes[1][var.w.byte1])
а вызов
S(var, str)
на
(SBoxes[str][var.w.bytestr])
и так далее