D. Infuehr Ответов: 2

Как передать функцию с фиксированным параметром другому методу в C?


Предположим, у меня есть метод print, который принимает void* в качестве аргумента:

Print(void *instance)


Как я могу установить член структуры в этот метод?

struct Foo
    {
        void(*Print)(); //this does not match to Print above!
       //void(*Print)(void* Instance); //This would but I cannot change this
    }


Так как же мне придется изменить этот метод:
void Init()
{
   struct Foo* k = malloc(sizeof(Foo));

   Foo->Print = &Print(k);   //does not work!
 //Foo->Pring = &Print;     //would work if types were the same and I really need the parameter k there.
    }


Я хочу установить указатель функции с помощью Фиксированный Аргумент.
Это должно было бы сделать действительно объектно подобное поведение в ANSI C для этого проекта:
Объектная ориентация в C?!

String* r = New_String ("Привет");
r - & gt;бесплатно(r);
Должен стать r - & gt;свободным();

Заранее спасибо

Что я уже пробовал:

Другие языки поддерживали бы его таким образом:

Foo. Print = () = & gt;Print(k);

Аргумент должен быть вставлен во время выполнения.
Любые нестандартные сумасшедшие способы приветствуются.

Например, найти экземпляр Foo struct из метода, который был использован для вызова Foo- & gt;Print();

Philippe Mori

Расскажите нам, почему вы не используете компилятор C++, если хотите использовать синтаксис C++.

Philippe Mori

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

2 Ответов

Рейтинг:
2

Philippe Mori

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

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

Неясно, пишете ли вы код на C или C++. Если вы пишете код на языке Си, то у вас все равно нет функции-члена, так что делать такие вещи бессмысленно. Если вы пишете код на C++, то в любом случае вам следовало бы использовать new вместо malloc...

Кроме того, это выглядит странно, что вы хотите установить указатель функции в struct point на глобальную функцию.

Очевидно, что это то, что вы хотите сделать, тогда это чрезвычайно просто (предполагая C++):

struct Foo
{
    void(*Print)(void* Instance);
    void PrintMe() { (*Print)(this); }
};

Если вы действительно хотите сделать это по-другому, то у вас будет статический член, принимающий параметр, который будет интерпретироваться как экземпляр:
struct Foo2
{
    static void PrintFuction(void *instance) 
    { 
        reinterpret_cast<foo2>(instance)->PrintMember();
    }
    static void PrintMember() { /* code here */ }
};


Кстати, в другом языке, таком как C#, компилятор, по сути, сгенерировал бы класс с данными в его членах. И такая функция будет представлена парой {function, instance}.

Кроме того, в современном C++ у вас также есть такой объект функции.

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

- Обновление (2016-10-07)—

Как указано в комментариях, невозможно достичь желаемого синтаксиса, используя только стандарт C.

Что касается "обратного вызова доступа к памяти", то я об этом не знал. Я нашел следующую информацию: http://stackoverflow.com/questions/6211429/callback-on-memory-access/6211502[^].

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

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

И даже тогда этот взлом может привести к проблемам с отладкой, обработкой ошибок или проблемами совместимости.

Это не то, что следует использовать для регулярной разработки или просто для удобства более приятного синтаксиса. Если это то, что вы хотите, то вам следует серьезно подумать о компиляторе C++.

Такой вид взлома может быть полезен, если разработать какую-то конкретную библиотеку для конкретной платформы для таких вещей, как RTOS, профилировщики, анализаторы трассировки стека... Но если вы написали такие инструменты, то вам нужно иметь глубокое понимание вашей платформы и компилятора.

Серьезно ... использует компилятор C++!

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

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

Альтернатива

Вы можете использовать компилятор C++, способный генерировать код на языке Си. Видеть http://stackoverflow.com/questions/3311563/do-all-c-compilers-generate-c-code[^] для получения дополнительной информации.


D. Infuehr

В C++ это было бы тривиально. Я хочу использовать C++ как синтаксис в C. Я изменил свой вопрос, чтобы прояснить.
В основном
String* r = New_String ("Привет");
r - & gt;бесплатно(r);
Должен стать r - & gt;свободным();
так что экземпляр не должен быть передан самим методам.

Philippe Mori

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

Если вы действительно хотите использовать синтаксис C++, то используйте компилятор C++. Если вы не можете использовать C++ на своей платформе (возможно, какое-то встроенное устройство), то вы можете найти компилятор C++, который генерирует код C для вас.

В противном случае вам придется написать свой собственный предварительный компилятор. Это решение, которое использовалось в начале C++ (я думаю, что оно называлось C++ с классами).

Philippe Mori

Вы должны немного понять, как генерируется код. В C++ компилятор будет помещать это в стек перед вызовом функции. Таким образом, компилятор C++ будет более или менее генерировать код, как если бы функция была дополнительным скрытым аргументом.
Очевидно, что компилятор C не будет выдвигать этот аргумент, поскольку он не будет служить никакой цели в коде C.
Таким образом, любой возможный взлом мог только ухудшить ситуацию. Вы можете только сделать код более трудным для чтения и обслуживания.

D. Infuehr

Я хочу достичь синтаксиса, подобного C++. Самым лучшим на данный момент является Foo->Print(Foo). Я хочу получить Foo внутри Print, не передавая его явно в качестве аргумента.
На форуме, где люди действительно отвечают на этот вопрос и не просто говорят мне, что это невозможно, кто-то указал, что я мог бы написать обратный вызов доступа к памяти, вызываемый при доступе к местоположению печати Foo - >, который толкает Foo в стек. Тогда я могу вызвать печать, как это должно работать. (например, этот указатель)
Кто-нибудь знает, как это сделать?

Philippe Mori

Я обновил свой ответ гораздо большей информацией...

Philippe Mori

Форумы, подобные этому, не поощряют плохую практику кодирования и взлома...

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

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

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

Рейтинг:
1

Richard MacCutchan

Сигнатура функции должна совпадать, иначе у вас может получиться что-то вроде:

void Print(void* pp)
{
	// do stuff
}
void Print(char* cp, int i)
{
	// do stuff
}

//

struct Foo
{
    void(*Print)(); // so which one does the compiler choose?
}


D. Infuehr

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

Richard MacCutchan

То, что вы хотите сделать, невозможно в C/C++. Вы должны следовать правилам языка.