Дублирование функций C / C ++ во время компиляции - PullRequest
3 голосов
/ 17 июня 2011

Если у меня есть функция A(), меня интересует поиск удобного метода для создания функции B(), которая имеет ту же функциональность, что и A(), отличающаяся только по имени. Новая функция будет для одноразового использования. Намерение состоит в том, чтобы различать вызовы одной и той же функции в несколько примитивном профилировщике выборки, и дублированная функция будет использоваться только в этом контексте. То есть он никогда не коснется производственного кода и будет использоваться только для повозок.

Первое предположение - это макрос, который объявляет функцию с именем B и создает внутри нее встроенный вызов A(). Проблема здесь заключается в том, что я не знаю метода в GCC для принудительного вызова произвольной функции inline; кажется, что все встроенные опции предназначены для объявлений функций, а не для вызовов.

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

Ответы [ 7 ]

7 голосов
/ 17 июня 2011

Использование шаблонов

template<int x>
void A()
{
    // ..
}

int main()
{
    A<0>();
    A<1>();
    return 0;
}

Обновление

Компилятор может быть слишком умным и создавать только одно тело для A <0> и A <1>.По крайней мере, Visual C ++ 2010 делает это в режиме выпуска.Чтобы предотвратить это, просто используйте параметр шаблона внутри тела шаблона функции в журналах или утверждениях.Например,

#include <iostream>

template<int x>
void A()
{
    ::std::cout << x << std::endl;
    // ..
}

int main()
{
    A<0>();
    A<1>();
    auto v0 = A<0>;
    auto v1 = A<1>;
    ::std::cout << v0 << std::endl;
    ::std::cout << v1 << std::endl;
    ::std::cout << (v0 == v1) << std::endl;
    return 0;
}
3 голосов
/ 17 июня 2011

Это работает с использованием шаблонов:

#include <iostream>                                                             

template<typename T>
void foo() {
    static int x = 0;
    std::cout << &x << std::endl;
}

int main(int argc, char **argv) {
    foo<int>();
    foo<float>();
    return 0;
}

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

2 голосов
/ 17 июня 2011

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

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

void Func() { .... }

_declspec(noinline) 
void A_Func( return Func(); }
void B_Func( return Func(); }
void C_Func( return Func(); }

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

2 голосов
/ 17 июня 2011

Если это одноразовый отладочный взлом, то почему бы и нет:

#define A_CONTENT \
    ... // whatever

void A()
{
    A_CONTENT
}

void B()
{
    A_CONTENT
}

...

A();  // Call to A
B();  // Call to B  

Макросы, как правило, мрачны, но мы не говорим о производственном коде, так кого это волнует?

1 голос
/ 17 июня 2011

Вы всегда можете определить макрос, например, в Chromium мы делаем следующее для повторного использования кода:

#define CHROMEG_CALLBACK_1(CLASS, RETURN, METHOD, SENDER, ARG1)     \
  static RETURN METHOD ## Thunk(SENDER sender, ARG1 one,            \
                                gpointer userdata) {                \
    return reinterpret_cast<CLASS*>(userdata)->METHOD(sender, one); \
  }                                                                 \
                                                                    \
  virtual RETURN METHOD(SENDER, ARG1);

И мы называем их как:

 CHROMEGTK_CALLBACK_1(PageActionViewGtk, gboolean, OnExposeEvent, GdkEventExpose*);

 CHROMEGTK_CALLBACK_1(PageActionViewGtk, gboolean, OnButtonPressed, GdkEventButton*);

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

0 голосов
/ 21 августа 2012

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

C ++ 11 также позволяет вам сделать это:

void A() {
    ...
}

...

auto B = [] () -> void { A(); };

Теперь вы можете использовать B синтаксически, как если бы это была функция, заключающая в себе A.

0 голосов
/ 17 июня 2011

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

Кроме того, макросы - это зло.Никогда не используйте их, если вы действительно не должны.

...