Страницы

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

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

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

Шифрование функций в проекте

#шифрование #метапрограммирование #самомодификация


Собсно сабж, кто сталкивался, как реализовать?

Требуется приложение с зашифрованым кодом (часть приложения начнет работать после
ввода ключа по которому функции расшифруются), по сути тут речь о морфировании кода
на "лету" в ходе выполнения приложения. желательно теория (книги, статьи), и примеры
(в идеале на паскалях). 
    


Ответы

Ответ 1



Самомодификация и шифрование кода идут друг с другом. Посмотрите пример самомодификации кода. Возможно пригодится полиморфный генератор.

Ответ 2



Такие фишки можно делать только когда есть виртуальная машина, на уровне которой инструкции зашифрованы или как-то модифицированы и потом в ходе выполнения дешифруются в инструкции процессора. Или как вариант когда есть специализированный процессор, который шифрует/дешифрует инструкции на лету. Такие спец.процессоры любят ребята из гебешных структур - вам туда :) Если рассматривать ситуацию попроще, то такой морфинг на лету можно делать на Java с кастомным загрузчиком классов - статья здесь P.S. Это конечно же не совсем морфинг, поскольку под "настоящим" морфингом я бы понимал хаотичную самомодификацию кода на лету. Это так сказать статический морфинг кода.

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

Создание рекурсивного шаблона

#cpp #cpp11 #шаблоны_с++ #метапрограммирование


У меня есть семейство параметризованных функций, типа:

template
struct SomeFunc {
    static void *call(void *arg) { return arg; }
};


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

В очень примитивной реализации это выглядит так:

template class F>
void *choose_from(void *arg, char run_time_type_of_arg) {
    switch (run_time_type_of_arg) {
        case 'i': return F::call(arg);
        case 'l': return F::call(arg);
        case 'd': return F::call(arg);
        default:  return nullptr;
    }
}


Вызывается это очень удобно:

void *test_some_func(void *arg, char run_time_type_of_arg) {
    return choose_from(arg, run_time_type_of_arg);
}


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

Поэтому я хочу переписать эту конструкцию с использованием type traits и более общих
шаблонов на их основе:

template struct TypeTrait { const static char value = 0; };
template<> struct TypeTrait { const static char value = 'i'; };
template<> struct TypeTrait { const static char value = 'l'; };
template<> struct TypeTrait { const static char value = 'd'; };

template class F, typename T1>
struct ChooseFrom1 {
    inline static void *call(void *arg, char run_time_type_of_arg) {
        if (run_time_type_of_arg == TypeTrait::value)
            return F::call(arg);
        else
            return nullptr;
    }
};

template class F, typename T1, typename T2>
struct ChooseFrom2 {
    inline static void *call(void *arg, char run_time_type_of_arg) {
        if (run_time_type_of_arg == TypeTrait::value)
            return F::call(arg);
        else
            return ChooseFrom1::call(arg, run_time_type_of_arg);
    }
};


За счет встраивания функций все вызовы полностью удаляются на этапе компиляции, и
в результате получается машинный код, почти полностью идентичный первой реализации
на switch/case (я проверял листинги - все так).
Но у меня не получилось рекурсивного шаблона (как видно ChooseFrom1 и ChooseFrom2
имеют разные имена).
Вызывать это можно, например, так (совершенно не удобно):

void *test_some_func_2(void *arg, char run_time_type_of_arg) {
    auto a = ChooseFrom1::call(arg, run_time_type_of_arg);
    if (a) return a;
    return ChooseFrom2::call(arg, run_time_type_of_arg);
}


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

void *test_some_func_ideal(void *arg, char run_time_type_of_arg) {
    return ChooseFrom::call(arg, run_time_type_of_arg);
}


Но все попытки совместить ChooseFrom1 и ChooseFrom2 ... ChooseFromN в один рекурсивный
шаблон (один обобщенный шаблон для самой рекурсии плюс одна его специализация для остановки
рекурсии) наталкиваются на ошибки компиляции.

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


Ответы

Ответ 1



Возможно, вам нужно это: template class F, typename... Types> struct ChooseFrom { }; template class F, typename T1, typename... TRest> struct ChooseFrom { inline static void *call(void *arg, char run_time_type_of_arg) { if (run_time_type_of_arg == TypeTrait::value) return F::call(arg); else return ChooseFrom::call(arg, run_time_type_of_arg); } }; template class F> struct ChooseFrom { inline static void *call(void *arg, char run_time_type_of_arg) { return nullptr; } }; С другой стороны, я бы на вашем месте постарался не доводить до передачи типа через эмуляцию typeid в рантайме, а протянуть тип как параметр шаблона в программе. При этом можно было бы по идее обойтись вовсе без ChooseFrom, и использовать прямо SomeFunc::call.

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

Перечисление полей структуры

#cpp #cpp14 #метапрограммирование


Есть структура и функции:

struct S {
    T1 t1;
    T2 t2;
    // и т.д.
} s;

template  f(const T& t, const char* name) { /* что-то */ }


Необходимо, чтобы работал следующий (псевдо)код:

for (поле : поля(s))
    f(поле.значение, поле.имя);


То есть, надо как-то перечислить все поля структуры, получая их тип, значение и имя
в виде строки.

Я знаю, что рефлексии в языке C++ нет. Но здесь не нужна рефлексия при выполнении,
нужна рефлексия на стадии компиляции или линковки. При этом можно как угодно извращаться
с объявлением структуры S, но нельзя трогать функцию f. Но в итоге всё равно должна
быть каким-либо образом создана либо структура либо что-то структурообразное, поволяющее
обращаться к полям (s.t1 и т.д.).

Сторонние библиотеки использовать нельзя, но можно использовать макросы, шаблоны
и вообще все доступные возможности C++14.
    


Ответы

Ответ 1



Как предложил в комментариях к вопросу @KoVadim, можно использовать так называемые X Macro. С их использованием Ваша задача решается примерно следующим образом: #include #define STRUCT_FIELDS \ X(int, i) \ X(float, f) \ X(const char*, c) struct S { #define X(type, name) type name; STRUCT_FIELDS #undef X }; template void f(const T& t, const char* name) { std::cout << name << " = " << t << std::endl; } void printStruct(const S& s) { #define X(type, name) f(s.name, #name); STRUCT_FIELDS #undef X } int main(int argc, char* argv[]) { S s; s.i = 1; s.f = 10.1; s.c = "s"; printStruct(s); } Результат работы программы: i = 1 f = 10.1 c = s

Ответ 2



Можно допилить мое решение. Оно предназначено для разбора параметров командной строки и помещения их в кортеж, к элементам которого можно обращаться по имени. При этом, имена на этапе компиляции заменяются на индексы элементов в кортеже. Как с этим работать: Нужно унаследовать класс от opt-parser #ifndef CONVOPTIONS_H #define CONVOPTIONS_H #include "opt-parser/optparser.h" struct optlist { static constexpr auto options=tuple_cat( OPTPARSER::makeO("input"), OPTPARSER::makeO("output") ); }; class ConvOptions:public OPTPARSER::Options { public: ConvOptions(int argc,char** argv); }; #endif // CONVOPTIONS_H После чего, можно обращаться к его полям по именам: ConvOptions opts(argc,argv); $(opts,"input")

Ответ 3



Это шкурка ответа с шагами его получения. Надо ещё везде распихать ссылки и добавить поддержку имён полей. http://ideone.com/6LOtIl http://ideone.com/ac32bj http://ideone.com/bG8q69 http://ideone.com/umN51y http://ideone.com/bxgu9H http://ideone.com/rddXvG #include #include using namespace std; template struct enumerate { static auto get(typed x) -> decltype(x.template get()) { return x.template get(); }; const static bool has_next = !is_same()), void>::value; typedef enumerate next; template