Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Генерирование функций


Автор: Lipetsk 3.11.2011, 07:51
Здравствуйте
У меня возникла такая задача на C/C++. Нужно сделать что-то вроде генератора функций. Например, 
Код

//как сделать такую функцию
void* Generator(int arg);

//которая, будет создавать функции вида
int f(){
  int a=arg;  //Значение arg должно вставляться в функцию f как константа во время генерации этой функции генератором Generator
  return a;
}
//и возвращать указатель на нее

//В итоге должно получиться:
typedef int (*func) (void);

func f7=(func) Generator(7);
func f11=(func) Generator(11);

printf("%i",f7());  //выводит "7"
printf("%i",f11());  //выводит "11"


Наверняка для этого существуют какие-то стандартные подходы, но в интернете что-то ничего удовлетворительного не нашел

Спасибо

Автор: Earnest 3.11.2011, 08:03
Во-первых, это надо спрашивать в разделе С++
Во-вторых, можно как-то так:
Код

template <int _ARG>
int f()
{
  int a = _ARG;  
  return a;
}

Использование:
Код

printf("%i",f<7>());  // выводит "7"

Автор: Lipetsk 3.11.2011, 08:16
на счет раздела, может я и ошибся

но ваш вариант реализации определенно не годится, нужно именно "генерировать" функции в real-time, а не при компиляции
нужно, чтобы функция f имела определенный тип, в примере это func, потому что это будет CallBack функция для API

Автор: Lipetsk 3.11.2011, 08:37
Если я ошибся разделом, то, пожалуйста, перенесите тему в раздел C/C++ Общие вопросы

Автор: Earnest 3.11.2011, 09:41
Цитата(Lipetsk @  3.11.2011,  09:16 Найти цитируемый пост)
нужно именно "генерировать" функции в real-time,

С++ так не умеет. А что касается раздела, то это может сделать только его модератор.

Автор: boostcoder 3.11.2011, 10:33
как сказала Earnest, в с++ невозможно генерировать функции в run-time. но это собственно и не надо.
одно из решений:
Код

#include <iostream>
#include <boost/bind.hpp>
#include <boost/function.hpp>

typedef boost::function<int()> func_sig;

int f(int arg = 0) {
   int a = arg;
   return a;
}

func_sig Generator(int arg) {
   return boost::bind(&f, arg);
}

int main() {
   func_sig f7= Generator(7);
   func_sig f11= Generator(11);
   
   std::cout << f7() << std::endl;
   std::cout << f11() << std::endl;
}

http://liveworkspace.org/code/3a57b623742d384def3c92ebca38b115

Автор: Lipetsk 3.11.2011, 10:54
Цитата(boostcoder @  3.11.2011,  08:33 Найти цитируемый пост)
как сказала Earnest, в с++ невозможно генерировать функции в run-time. но это собственно и не надо.
одно из решений:

не подходит такое решение, т.к. тип функции f должен быть typedef int (*func) (void);

Автор: boostcoder 3.11.2011, 11:03
Цитата(Lipetsk @  3.11.2011,  10:54 Найти цитируемый пост)
тип функции f должен быть typedef int (*func) (void);

обоснование, пожалуйста ;)

Автор: Lipetsk 3.11.2011, 11:16
Цитата(boostcoder @  3.11.2011,  09:03 Найти цитируемый пост)
обоснование, пожалуйста ;) 

я уже писал
Цитата(Lipetsk @  3.11.2011,  06:16 Найти цитируемый пост)
потому что это будет CallBack функция для API


Автор: Леопольд 3.11.2011, 11:20
Цитата(Lipetsk @  3.11.2011,  11:16 Найти цитируемый пост)
потому что это будет CallBack функция для API
А чем предложенный вариант не подходит?
Код
typedef boost::function<int()> func;
Этот вариант работает не только с указателями на функции но и с функторами (boost::bind генерирует функторы из функций), например:
Код
void APIfunc(std::function<void()> callback);

class Functor
{
    void operator()() {}
};

void entry()
{
    //...
    APIfunc(Functor());
    //...
}
Вплне нормальная ситуация в С++, пример тому STL.

Автор: Lipetsk 3.11.2011, 11:24
Леопольд, вы не видите разности между
Код

typedef int (*func) (void);

и
Код

typedef boost::function<int()> func;

?

Автор: Леопольд 3.11.2011, 11:26
Цитата(Lipetsk @  3.11.2011,  11:24 Найти цитируемый пост)
Леопольд, вы не видите разности между
Голый указатель на функцию это из мира чистого С, в котором нет поддержки ООП. Если не нравится boost::function и не хочется привязываться к сторонней библиотеке, можно использовать шаблоны:
Код
template<typename Callback>
void APIfunc(Callback callback)
{
    //...
    callback();
    //...
}
Такой шаблон тоже будет работать и с указателями на функции и с функциональными объектами. Но "недостаток" его в том, что вся реализация будет в хидерах.

Автор: Lipetsk 3.11.2011, 11:43
Цитата(Леопольд @  3.11.2011,  09:26 Найти цитируемый пост)
Голый указатель на функцию это из мира чистого С, в котором нет поддержки ООП

А мне и не нужно ООП
например, мне нужно в классе вызвать DdeInitialize и передать ему уникальную callback функцию, привязанную к экземпляру класса, а не статическую. Эту уникальную функцию в принципе можно сгенерировать используя ассемблерные вставки в код. Но не очень хочется изобретать велосипед, хотя  smile  я уже двигаюсь в этом направлении

Автор: boostcoder 3.11.2011, 12:19
Lipetsk, а диапазон значений для функции Generator(), известен?

Автор: Lipetsk 3.11.2011, 12:44
Цитата(boostcoder @ 3.11.2011,  10:19)
Lipetsk, а диапазон значений для функции Generator(), известен?

я не понял вопроса
ее значения это функции
много ли экземпляров функций f я собираюсь создать? пока не много
но скоро мне нужно будет заняться одной задачей с COM-объектами, в которой, я планирую, будет аналогичный генератор и вызывать его я буду постоянно (а также удалять отработавшие функции)

Автор: Леопольд 3.11.2011, 12:58
Цитата(Lipetsk @  3.11.2011,  11:43 Найти цитируемый пост)
А мне и не нужно ООП
например, мне нужно в классе вызвать DdeInitialize и передать ему уникальную callback функцию, привязанную к экземпляру класса, а не статическую.
Без ООП переносимого решения не получить.

Автор: Lipetsk 3.11.2011, 13:30
Цитата(Леопольд @  3.11.2011,  10:58 Найти цитируемый пост)
Без ООП переносимого решения не получить.

А директивы компилятора на что?

Автор: bsa 3.11.2011, 13:45
Цитата(Lipetsk @  3.11.2011,  14:30 Найти цитируемый пост)
А директивы компилятора на что? 

какие например? И вообще, какие стандартные (читай переносимые) директивы компилятора ты знаешь?

Автор: math64 3.11.2011, 14:05
Как раз важно сколько экземпляров функции нужно создать. если оно ограничено, то можно.
Код

typedef int (*pfunc)(void);
int results[16];
int usedmask = 0;
int f0(void) { return results[0]; }
int f1(void) { return results[1]; }
int f2(void) { return results[2]; }
...
int f15(void) { return results[15]; }
func functions[] = { f0, f1, f2, ..., f15};
func generator(int res) {
  for(int i=0; i < 15; i++) {
    if ( (usedmask & (1 << i)) == 0) {
      usedmask |= 1 << i;
      results[i] = res;
      return functions[i];
    }
  }
  return NULL;
}
void freegenerated(func f) {
  for(int i=0; i < 15; i++) {
    if (functions[i] == func) {
      usedmask &= ~(1 << i);
    }
  }
}

Автор: mes 3.11.2011, 14:55
Lipetsk, Вы не очень четко сформулировали задачи, поэтому Вас не понимают и предлагают пол-решения..
Вы б вместо того чтоб возмущаться на счет типов лучше бы прояснили немного.. 
Я так  понимаю Вам нужен коллбяк посредник, дополняющий к аргументам, еще свой идентификатор.. так ?

Автор: mes 3.11.2011, 15:12
алсо наглядный пример вызова функции (без использования ваших идей с генератором) был бы в помощь..
т.е. Вам нужно точно определить, что есть на входе, и что Вамнужно на выходе и  тогда можно будет ответить как этого добиться оптимальным способом..

Добавлено через 56 секунд
насчет невозможности переносимого решения, написано не (совсем ) правда.. 



Автор: boostcoder 3.11.2011, 15:40
Lipetsk, объясни нормально задачу. не верю я что тебе реально нужен генератор каких-то функций.

Автор: baldina 3.11.2011, 15:55
имхо ему нужно доп. параметры в колбэк передавать, а DdeInitialize такой возможности не предоставляет. Вот и выдумывает "генерацию функций"

Автор: bsa 3.11.2011, 16:04
Lipetsk, скорее всего, такое сделать просто нельзя, так как предполагается, что DdeInitialize вызывается единожды для приложения (я ошибаюсь?), поэтому все данные коллбэка должны быть статическими, так как он един для всего процесса. Если ты не знаешь, как это сделать с классами - почитай про PIMPL.

Автор: Lipetsk 3.11.2011, 22:33
Цитата(math64 @  3.11.2011,  12:05 Найти цитируемый пост)
Как раз важно сколько экземпляров функции нужно создать. если оно ограничено, то можно.

суть такая, но нужна динамика, а не фиксированный набор функций


Цитата(bsa @  3.11.2011,  14:04 Найти цитируемый пост)
DdeInitialize вызывается единожды для приложения

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

но я уже кое-что нашел
http://www.hackcraft.net/cpp/windowsThunk/
и
http://www.codeproject.com/KB/cpp/Thunk_uses.aspx

Автор: mes 3.11.2011, 23:37
Цитата(Lipetsk @  3.11.2011,  21:33 Найти цитируемый пост)
но я уже кое-что нашел

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


Автор: Lipetsk 4.11.2011, 10:36
Вот мое точное условие
Цитата(Lipetsk @  3.11.2011,  05:51 Найти цитируемый пост)


В случае, когда функции не генерируются динамически, а создаются заранее получается примерно так (мне это не нужно, даю ссылку для лучшего понимания задачи)
Цитата(math64 @  3.11.2011,  12:05 Найти цитируемый пост)


Автор: mes 4.11.2011, 10:47
Lipetsk, в цитатнике текста не видно..

Автор: boostcoder 4.11.2011, 12:30
Цитата(mes @  4.11.2011,  10:47 Найти цитируемый пост)
в цитатнике текста не видно..

все потому, что его там нет.

Автор: math64 4.11.2011, 12:55
Динамически создать функцию в условиях Windows не пролучится, даже с ассемблером, в DOS было можно примерно так:
Код

typedef int (*func)(void);
int callback(void) { return 0x1234; }
void next(void) {}

func generate(int value) {
char* start = (char*)&callback;
char* end = (char*)&next;
int len = end - start;
char* code = new char[len];
memcpy(code, start, len);
// ищем в коде 0x1234 заменяем на value
return (func)code;
}

в Windows так не получится, поскольку у сгенерированного кода не будет прав на выполнение.

Автор: baldina 4.11.2011, 13:26
Цитата(Lipetsk @  3.11.2011,  22:33 Найти цитируемый пост)
DdeInitialize можно вызывать несколько раз, как тогда быть

тогда каждый вызов DdeInitialize обязательно будет связан с уникальным идентификатором

Автор: mes 4.11.2011, 14:49
Цитата(Lipetsk @  3.11.2011,  07:16 Найти цитируемый пост)
нужно именно "генерировать" функции в real-time, а не при компиляции
нужно, чтобы функция f имела определенный тип, в примере это func, потому что это будет CallBack функция для API

насколько я понял, имеется некое апи с колбяком,
допустим :
Код

typedef void callback();
void api(callback* cb)
{
   cb();
}

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

тогда решение может быть таким :
Код


namespace w {

typedef std::function<void()> callback;
   
void  api (callback fn)
{    
   static callback _fn;     
   struct L {  static void handle() { _fn(); }  };
      
   _fn = fn;

   ::api(&L::handle);   
   
}
}


http://liveworkspace.org/code/2bc57970c9075670290cdc23bf2871eb

Автор: math64 4.11.2011, 15:28
mes, зачеи эти извращения? они экивалентны
Код

std::function<void()> _fn;
void fn() { _fn(); }

std::function<void()> fn2;
std::function<void()> fn3;

_fn = fn2;
api(fn);
_fn = fn3;
api(fn);

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

Автор: mes 4.11.2011, 15:43
Цитата(math64 @  4.11.2011,  14:28 Найти цитируемый пост)
 зачеи эти извращения?

для визуальной эквивалентности апи-надстройки к базовому апи  smile 
да и чтоб автору был повод пересмотреть свое отношение к полноте задаваемого вопроса.. 
тс несколько раз просили сформулировать задачу, он _вежливо_ отказался,  поэтому решал ее со своей колокольни smile


Автор: mes 4.11.2011, 16:05
Цитата(math64 @  4.11.2011,  14:28 Найти цитируемый пост)
зачеи эти извращения?

ну раз то извращение, тогда добавлю еще  smile  :
http://liveworkspace.org/code/7fbb6089dd53eae19c9ee68628744793

Добавлено @ 16:06
Код

template<apifn* fn>   
struct call
{          
   static callback _cb; 
   static void handle() { _cb(); } 
           
   void operator() (callback cb) {
    _cb = cb;
     fn(&handle);   
   }   
};
template<apifn* fn>  
callback call<fn>::_cb;

call<&::api1> api1;
call<&::api2> api2;

}


или так в двух вариантах :
http://liveworkspace.org/code/0ed044826500e39651a435224322268c

 smile 
ух сколько наболтал: 7007   smile 

Автор: math64 4.11.2011, 19:04
Я бы не назвал это извращением, если бы оно решало проблему. API скорее всего имееи вид:
Код

typedef void callback();
class API {
public:
  API(callback* cb) { m_cb = cb; }
  ~API() {}
  void doSomething() { cb(); }
private:
  callback* m_cb;
};
extern "C" {
  void* apiInit(callback* cb) { return new API(cb); }
  void apiDoSomething(void*p) { ((API*)p)->doSomething(); }
  void apiDestroy(void*p) { delete (API*)p; }
}

Обычно делают так:
Код

typedef void callback(void*p);
class API {
public:
  API(callback* cb, void* p) { m_cb = cb; m_p = p; }
  ~API() {}
  void doSomething() { cb(p); }
private:
  callback* m_cb;
  void* m_p;
};
extern "C" {
  void* apiInit(callback* cb, void* p) { return new API(cb, p); }
  void apiDoSomething(void*api) { ((API*)api)->doSomething(); }
  void apiDestroy(void*api) { delete (API*)api; }
}

Тогда использование выглядит так:
Код

class Callback {
public:
  Callback() { api = apiInit(handle, this); }
  ~Callback() { apiDestroy(api); }
  void doSomething() { apiDoSomething(api); }
private:
  static void handle(void*p) { Callback* cb = (Callback*)p; cb->doSomethingImpl(); }
  void doSomethingImpl();
  void* api;
};

Но если у callback не предусмотрен лишний аргумент, возникает проблема с его использованием.
Если число экземпляров Callback ограничено - решение см. мой пост на предыдущей странице.

Автор: mes 4.11.2011, 21:39
math64,  smile 
попробую разобраться что Ва написали.. напервый взгляд выглядит немного несвязанно..
smile

Добавлено через 5 минут и 44 секунды
вроде понял о чем Вы..

Добавлено через 11 минут и 17 секунд
вообщем требуется чтоб заработал такой условный код с расскомментированной строкой
Код

typedef void callback();
void api(callback *cb)
{
   cb();
};

struct A {

  void fn () {  //  api ( do_fn(); ) 
  }
  
  void do_fn() { std::cout << i; }  
  int i;
};

Автор: Lipetsk 7.11.2011, 11:20
Цитата(math64 @  4.11.2011,  10:55 Найти цитируемый пост)
Динамически создать функцию в условиях Windows не пролучится, даже с ассемблером

а у меня получается, и функция в области данных выполняется нормально

Автор: boostcoder 7.11.2011, 11:27
Lipetsk, показывай решение.

Автор: Lipetsk 7.11.2011, 11:47
полного решения пока нет, не дают "работать"
но такой код легко выполняется

Код

typedef void (*func) (void);
struct fCall{
  char*c;
  fCall():c("123456"){};
  void f(void){
    c[0]=0xc3;
    ((func)(void*)c)();
  }
};


int main()
{ 
   fCall f;
   f.f(); 
}


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

Автор: math64 7.11.2011, 12:01
В Линуксе такое не работает - "Ошибка сегментирования"; в Windows тоже не должно бы, только в режиме DOS.

Добавлено через 14 минут и 21 секунду
А если и получается - это дыра в защите Windows. Как только её закроют, работать перестанет.

Автор: bsa 7.11.2011, 13:00
Цитата(math64 @  7.11.2011,  13:01 Найти цитируемый пост)
в Windows тоже не должно бы, только в режиме DOS.

где-то кто-то говорил, что ряд компиляторов (или это особенность именно ОС?) (МС в частности) размещает Си-строки в записываемой области памяти в целях совместимости со старым кривым софтом.

Цитата(Lipetsk @  7.11.2011,  12:47 Найти цитируемый пост)
т.е. в буфер c можно записать что угодно и потом выполнить как функцию 

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

Автор: boostcoder 7.11.2011, 13:15
Lipetsk, это тупиковое решение.
ищи другое smile 

Автор: bsa 7.11.2011, 13:33
Имхо, надо искать решение вообще в другой области. Что-то есть у меня подозрение, что DdeInitialize приложению достаточно вызвать всего один раз... А не на каждый чих. Возможно, даже система так устроена, что не предполагается более одного обработчика сообщений для приложения.

Если уж нужно делать несколько обработчиков, то можно сделать диспетчер сообщений. Все обработчики при создании регистрируются в нем, а при уничтожении отменяют регистрацию. Когда приходит сообщение, то диспетчер выбирает подходящий обработчик (или перебирает все, пока кто-то не обработает) и его вызывает. В этом случае callback всего один - статический приватный метод диспетчера.

Автор: Earnest 7.11.2011, 13:45
Цитата(bsa @  7.11.2011,  14:00 Найти цитируемый пост)
так как при попытке передать туда управление система быстро прикроет твое приложение. 

Цитата(math64 @  7.11.2011,  13:01 Найти цитируемый пост)
А если и получается - это дыра в защите Windows. Как только её закроют, работать перестанет. 

Не закроют, ибо совместимость. Это называется DEP (data execution protection) и регулируется в настройках ОС. На серверах по умолчанию запрещено, на рабочих станциях - разрешено, но и там и там регулируется. Но по сути - запуск на выполнение "данных" нужно использовать аккуратно, и только если того настоятельно требует задача (в целях защиты, например). Для данной же задачи - решение неудачное.  

Автор: Lipetsk 7.11.2011, 13:48
Я уже и сам понимаю, что такое не должно работать, хоть оно и работает
Просто казалось, что "Генераторы функций"  smile возможны, но значит нет

Всем большое спасибо за участие

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)