Comments 29
Как не используя макросы препроцессора С/С++ можно переопределить имя функции как при её вызове, так и для операции взятия адреса функции?
Написать другую функцию, вызывающую нужную
template<typename... Args>
decltype(auto) foo(Args&&... args)
{
return bar(std::forward<Args>(args)...);
}
Это будет работать для вызова функции, но как получить адрес изначальной функции, ведь при операции взятия адреса будет возвращен адрес переопределенной функции foo, а не bar?
Задача решена так, как она поставлена: и при вызове, и при взятии адреса.
А вообще, я занимаюсь программированием, а не извращениями в коде. Практическая польза данной "задачи" мне не ясна.
Решаемая задача - заменить в коде один идентификатор на другой во всех случаях возможного использования.
Это легко решается с помощью макросов, но в данном сценарии не получается с помощью инструментов, которые рекомендуется использовать вместо макросов.
Пункт "refactor/rename" в IDE?
А вообще – рефакторить код, чтобы не хотелось странного. Ну а пока нужен хак – пользоваться макросами, просто минимизировать их количество и максимально упростить.
Решаемая задача - заменить в коде один идентификатор на другой во всех случаях возможного использования.
А это вы вообще озвучиваете задачу рефакторинга.
Это легко решается с помощью макросов, но в данном сценарии не
получается с помощью инструментов, которые рекомендуется использовать
вместо макросов.
Ниже показывают, что возможно. Но вот необходимость подобного извращения вами не объяснена.
Сделать два разных макроса.
#include <cstdio>
#include <utility>
template<typename R, typename ...Args>
struct FunctionWrapper {
using FunctionType = R(*)(Args...);
FunctionWrapper(FunctionType func) : _func(func) {}
R operator()(Args... args) {
return _func(std::forward<Args...>(args...));
}
FunctionType operator &() {
return _func;
}
FunctionType _func;
};
int foo(int arg){
return arg;
}
FunctionWrapper bar{foo};
int main() {
printf("%p\n", &foo);
printf("%p\n", &bar);
}
Но не работают стандартные значения параметров
Если отказаться от ненужного & для получения адреса функции – кажется, задача заметно упростится.
Непонятна ваша цель. Для чего вам это? Это ваш код? Легаси? Или не ваш? К примеру, если ваш и вы пишете проект с нуля, в коде которого нужна такая возможность, то можно просто условиться, что есть макрос foo() который используется для вызова, а есть парный ему foo_ для взятия адреса.
И вообще, зачем вам макрос со скобками?
#define foo new_foo
Прекрасно работает и так foo(), и так &foo
P.s.
Есть ещё такая вещь, как кодогенератор. По сути - это внешний макродвижок, который обрабатывает файл перед его компиляцией. Там вы можете какие угодно правила реализовать.
Если бы не одно но. Когда требуется взять адрес функции, то её имя нужно указывать без скобок! И если имя этой функции требуется переопределять с помощью макропроцессора, то тут вариант только один, определять нужно только одно имя без скобок и каких либо аргументов.
Имя функции -- это символ с определённым типом. Никаких ограничений на счёт скобок для него нет. Может быть сколько угодно круглых скобок...
Как не используя макросы препроцессора С/С++ можно переопределить имя функции как при её вызове, так и для операции взятия адреса функции?
Операция взятия адреса для функции скорей не нужна, функция автоматически превращается в указатель на функцию в C/C++...
Как-как. Если в C++, то можно с использованием ссылки на другую функцию. Ссылки с нужным именем.
Если в голом-C, то можно с использованием константного указателя на функцию.
Юнит-тест системы часто пользуются такими методами для mocking'а функций.
PS: если нужно что-то сделать с аргументами, то сделать это в своей промежуточной функции. Если что-то сделать с аргументами нужно в месте вызова, то очевидно, это не выполнимо без макроса (или, может быть, но не всегда, шаблона в C++). И если нужен именно макрос то задача скорей решения не имеет.
PPS: сформулируйте задачу точнее. А то может в упор не видно очевидного решения.
В gcc можно сказать не использовать стандартный препроцессор и подпихнуть ему свой, который (например) может вызвать штатный препроцессор и потом добавить пару своих правок. Подробнее можно смотреть опцию -no-integrated-cpp (Preprocessor Options (Using the GNU Compiler Collection (GCC)) ). Из минусов - вы будете привязаны к собственной системе сборки.
Также есть (почти аналогичный) вариант: написать собственную утилиту, которая всё нужное подставит. Для примера смотрим moc на Qt5.
Еще вариант посмотреть на средства для алиасинга символов на стороне конкретного компилятора. Эти штуки пока не стандартизированы, но есть предложения, например, https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2729.htm
extern "C" int foo(int a) {
return a;
}
#ifdef _WIN32
#pragma comment(linker, "/alternatename:bar=foo")
int bar(int);
#else
int bar(int a) __attribute__((alias("foo")));
#endif
int main() {
foo(1);
bar(1);
assert(&foo == &bar);
}
Но там есть свои нюансы. В С++ на GCC/Clang нужно будет указывать mangled имя https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html.
https://godbolt.org/z/sGPY3PdYM
Да, подобный вариант скорее всего должен помочь, и самое главное без использования препроцессора и перегрузки операций как в шаблонах!
Алиас достаточно бесполезная штука в данном случае. Он позволяет лишь создать второй символ который будет ссылаться на тот же адрес, что другой глобальный символ, уже определённый в этой единице трансляции. Такого же можно достичь без алиаса, с помощью asm-вставки: декларируем extern-символ (не обязательно функцию), а в асме его определяем где и как нужно. И его даже локальным сделать можно в асме. Но все трюки не позволят заменить один символ на другой и невозможно сделать алиас для не определённого в текущей единице трансляции символа.
Можно ещё воспользоваться программой objcopy и у ней есть опция позволяющая переименовывать символы. Соответственно, это можно сделать в мейке после компиляции, но до линковки. Такое обычно нужно, если есть конфликт по символам с внешней библиотекой.
Ещё может помочь опция линкера -Wl,--wrap=symbol, кстати. Все ссылки на symbol будут вести в __wrap_symbol (который нужно определить самостоятельно), а старую функцию можно вызвать как __real_symbol. Такое обычно используется когда нужно переопределить библиотечную функцию или делаются mock-функции.
Ещё может помочь опция линкера -Wl,--wrap=symbol, кстати. Все ссылки на
symbol будут вести в __wrap_symbol (который нужно определить
самостоятельно), а старую функцию можно вызвать как __real_symbol.
Такое обычно используется когда нужно переопределить библиотечную
функцию или делаются mock-функции.
Во, большущее спасибо за хорошую мысль! Что-то я совсем про линкер не подумал, а ведь действительно замена символа должна решить эти проблемы!
Мне кажется, что я перепробовал все варианты, но решения так и не нашел.
А пробовали, как выше предлагает @fk0, объявить ссылку на функцию? Как-то так:
#include <cstdio>
int foo(float x) {
return 0;
}
constexpr const auto& bar = foo;
int main() {
foo(1.0f);
bar(1.0f);
std::printf("%p\n", foo);
std::printf("%p\n", bar);
std::printf("%p\n", &foo);
std::printf("%p\n", bar);
}
Этот код будет работать, но нужно переопределять уже существущую функцию, а не делать новую функцию как ссылку на старую реализацию.
Что за набор слов? Что такое "переопределять функцию", если необходим адрес "старой" функции?
Ссылка позволит иметь "альтернативное имя" для функции. И может ссылаться на переопределённую функцию. Что тут непонятного?
Идея в том, что например, переопределённую функцию сложно сделать static в пределах каждой единицы компиляции (чтоб не попадало в глобальные символы). А такую ссылку/указатель в каждую единицу компиляции положить можно. И в отдельную единицу компиляции функцию на которую она ссылается.
Тогда после линковки в коде может остаться и оригинальная функция, и новая, и весь код который использовал оригинальную функцию теперь будет вызывать новую. Ну тут нужно сделать так, чтоб у компилятора не было неоднозначности с определеним использовать ссылку или оригинальную функцию. Если оригинальная функция в хедерах не видна -- нет проблем. Если видна, то только при включении проблемного хедера в конкретном .c файле сделать #define function __function__, например, чтоб старая функция не конфликтовала с именем ссылки или указателя. И всё.
Основная проблема была в том, чтобы иметь новую функцию под старым именем. И чтобы для старого имени были доступны обращения как к функции (с указанием скобок), так и как к объекту для оператора получения адреса.
В приведенном вами примере наверно может получится реализовать переопределение с помощью статической функции, но мне кажется что получается неоправданно сложное решение.
Была похожая проблема. Отладка сгенерированных макросами методов - то ещё БДСМ. Тупо заменил макросы prebuild bash скриптом и воткнул его в свою систему сборки.
namespace v1
{
int func();
}
inline namespace v2
{
int func();
}
Неожиданная проблема с макросами (точнее без макросов)