2015-04-15 2 views
2

Я хочу создать систему плагинов с возможностью переопределить метод во время выполнения.Можно ли заменить метод во время выполнения?

Некоторые ответы говорят о указателях функций, но как насчет определенной функции или класса?

Как это:

class foo 
{ 
    public: 
    bar(int foobar); 
} 

Есть ли способ, чтобы получить указатель на функцию для этого, или заменить его?

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

+7

C и C++ - разные языки. –

+1

Возможно, и это специфичная платформа. Он используется, например, в ядре Linux (ksplice).Это больше похоже на исполняемый формат, чем на язык. PS: Чего вы хотите достичь? – ibre5041

+1

@ ibre5041: Ядро Linux не использует C++, что значительно упрощает работу. Например, нет (оператор) перегрузки. – MSalters

ответ

5

время выполнения функции «замена» может быть достигнута с одним из нескольких способов:

  1. Polymorphism
  2. Стандартные библиотечные объекты, такие как std::function
  3. вызовы сторонних библиотек или специальные методы платформы для перевода или отправки функции ,

На какой вариант лучше всего сильно зависит от предполагаемого использования и целевых сред. Например; плагиновые системы вполне могли бы использовать полиморфизм (с соответствующими фабриками и, возможно, даже в сочетании с template method pattern), в то время как внутренняя функция маршрутизации могла бы использовать std::function.

Эти методы не будут действительно «заменять» любую из функций, а могут быть настроены во время выполнения, чтобы направить вызов функции по мере необходимости.

Примечание Я сосредоточился на аспектах C++ вопроса (он был помечен C и C++, но пример кода - C++).

9

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

Вы можете заменить то, что делает метод, используя polymorphism или любым другим способом до настроить объект.

Заканчивать ответы на следующий вопрос: What's safe for a C++ plug-in system?

0

Я предполагаю, что это не возможно для C/C++.

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

Я считаю, что языки интерпретации хороши.

1

C не имеет каких-либо методов (только функции), так что ваш вопрос не имеет смысла в С.

В C++11, предполагая, что метод будет изменен в virtual, и предполагается, что ваш C++ реализация использует vtable указатель, расположенный при запуске объекта (это часто бывает с GCC в Linux), и если оба старых и новых класса имеют одинаковый размер и используют одно наследование из общего базового класса (например, FooBase), вы можете использовать оператор размещения new , поэтому в вашей основной программе:

class FooBase { 
    virtual ~FooBase(); 
    virtual int bar(int); 
    /// etc 
} 

class ProgramFoo : public FooBase { 
virtual ~ProgramFoo(); 
virtual int bar (int); 
/// other fields and methods 
}; 

и в плагине:

class PluginFoo : public FooBase { 
virtual ~ProgramFoo(); 
virtual int bar (int); 
/// other fields and methods 
static_assert(sizeof(PluginFoo) == sizeof(ProgramFoo), 
       "invalid PluginFoo size"); 
}; 

, то вы можете иметь некоторые плагин функции, как

extern "C" FooBase*mutate_foo(ProgramFoo*basep) 
{ 
    basep->~ProgramFoo(); // destroy in place, but don't release memory 
    return new(basep) PluginFoo(); // reconstruct in same place 
} 

это, мы надеемся, будет превалировать старый vptr по новой.

но это пахнет плохо, вероятно, undefined behavior в соответствии со стандартом C++ 11, но может работать на некоторые C++ реализаций, и, конечно, зависит от конкретной реализации. Я не рекомендую кодирование таким образом, даже если иногда это может случиться «работать».

Идиоматическим способом было бы использовать указатели на функции-члены или C++ 11 closures.

Похоже, что ваша архитектура плагина неправильно спроектирована. Посмотрите на Qt plugins для хорошего вдохновения.

4

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

#include <iostream> 
#include <functional> 

class Foo 
{ 
private: 
    void default_bar(int value) 
    { 
     std::cout << "The default function called\n"; 
    } 
    std::function<void(Foo*, int)> the_function = &Foo::default_bar; 
public: 
    void replace_bar(std::function<void(Foo*, int)> new_func) 
    { 
     the_function = new_func; 
    } 
    void bar(int value) 
    { 
     the_function(this, value); 
    } 
    void baz(int value) 
    { 
     std::cout << "baz called\n"; 
    } 
}; 

void non_member(Foo* self, int value) 
{ 
    std::cout << "non-member called\n"; 
} 

int main() 
{ 
    Foo f; 
    f.bar(2); 
    f.replace_bar(&Foo::baz); 
    f.bar(2); 
    f.replace_bar(non_member); 
    f.bar(2); 
    f.replace_bar([](Foo* self, int value){ std::cout << "Lambda called\n"; }); 
    f.bar(2); 
} 

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

2

На исходный вопрос: «Можно ли заменить метод во время выполнения на C/C++ ", возможно, и для него есть некоторые прецеденты, но (как говорили другие) большинство из этих случаев использования не относятся к вам. Это также не очень просто.

Например, linux kernell может использовать что-то под названием kpatch или kGraft. Это довольно сложный механизм, который, конечно же, не очень портативен и не очень полезен в программах пользовательского пространства, поскольку эти методы основаны на механизмах, запеченных в linux kernell.

1

также смотрите вариант компиляции MSVC/hotpatch. Это создаст код, в котором каждый неинтегрированный метод начинается с команды, имеющей не менее 2 байтов (короткий jmp относительный 0 - то есть NOP). Затем вы можете переписать образ запущенного приложения, и вы можете хранить длинный jmp на своей новой версии вашего метода.

Create Hotpatchable Image. См

Кроме того, например, на Linux вы (давно было) две функции под названием «Еореп». Один из них был определен в библиотеке glibc, а другой - в libpthread. Более поздний был поточно-безопасным. И когда вы распаковываете libpthread, функция «jumper» на функцию «fopen» перезаписывается, и используется функция libpthread.

Но это действительно зависит от вашей цели.

+0

И с '/ ob0', inlining отключается, поэтому каждый метод может быть настроен на hotpatched. Недостаток: 'vector :: operator []' тоже будет горячим, что, вероятно, не то, что вы хотели. – MSalters

+0

http://blogs.msdn.com/b/freik/archive/2006/03/07/x64-hotpatchability.aspx –

Смежные вопросы