Ці можна замяніць метад падчас выканання?

Я хачу зрабіць сістэму убудоў з магчымасцю пераазначэння метаду падчас выканання.

Некаторыя адказы кажуць паказальнікі на функцыі, але як наконт пэўнай функцыі або класа?

Як гэта:

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

Ці ёсць спосаб, каб атрымаць паказальнік на функцыю для гэтага, ці замяніць яго?

Дарэчы, замацоўваць не лічыць адказам, таму што гэта вельмі канкрэтнай платформы і небяспечна.

3
@ Ibre5041: Ядро Linux не выкарыстоўвае C ++, што значна спрашчае рэчы. Напрыклад, няма (аператар) перагрузкі.
дададзена аўтар MSalters, крыніца
C і C ++ розных моў.
дададзена аўтар Yu Hao, крыніца
Цалкам магчыма, і гэта залежыць ад платформы. Ён выкарыстоўваецца, напрыклад, у Linux ядры (Ksplice). Гэта больш адносіцца да выкананага фармату, чым мова. PS: што вы хочаце дасягнуць?
дададзена аўтар ibre5041, крыніца

8 адказы

Для таго, каб зрабіць сістэму убудоў вам не трэба замяніць метад класа падчас выканання.

Вы можаце замяніць, што робіць метад з дапамогай палімарфізму або любы іншы шлях да настройка аб'ект.

Check out the answers to the following question: What's safe for a C++ plug-in system?

9
дададзена

Функцыя «замену» Runtime можа быць дасягнута з адным з некалькіх спосабаў:

  1. Polymorphism
  2. Standard library facilities such as std::function
  3. Third party libraries or platform specific techniques to translate or dispatch function calls.

На які варыянт лепш моцна залежыць ад меркаванага выкарыстання і мэтавых асяроддзях. Напрыклад; убудова сістэма таксама можа выкарыстоўваць палімарфізм (з адпаведнымі заводамі і, магчыма, нават у спалучэнні з шаблон метаду шаблону </а >) у той час як унутраная маршрутызацыя функцыі можа выкарыстоўваць станд :: функцыю .

Гэтыя метады не сапраўды «замяніць» якія-небудзь з функцый, а можна наладзіць падчас выканання для маршрутызацыі выкліку функцыі па меры неабходнасці.

Note I've focused on the

C++ aspects of the question (it was tagged C and C++ but the sample code is C++).

5
дададзена

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

#include 
#include 

class Foo
{
private:
    void default_bar(int value)
    {
        std::cout << "The default function called\n";
    }
    std::function the_function = &Foo::default_bar;
public:
    void replace_bar(std::function 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 статычны (нават лепш, зрабіць гэта статычны метад, які вяртае статычную зменную, каб пазбегнуць статычнай ініцыялізацыі)

4
дададзена

Вярнуцца да першапачатковага пытанні: «Ці можна замяніць метад падчас выканання ў C/C ++» можна і ёсць некаторыя выкарыстоўваюць футляры для яго, але (як ужо казалі іншыя), большасць з гэтых выпадкаў выкарыстання не прымяняюцца цябе. Гэта таксама не вельмі проста.

Напрыклад, Linux kernell можа выкарыстоўваць тое, што называецца kpatch або kGraft . Гэта даволі складаны механізм --- які, вядома, не вельмі кампактны, і не вельмі карыснай у карыстацкай прасторы праграм, так як гэтыя метады абапіраюцца на механізмы запечаныя ў Linux kernell.

2
дададзена

Я мяркую, што гэта не магчыма для C/C ++.

Функцыянальны код быў скампіляваны ў двайковы, вы не можаце змяніць яго нагадванні падчас выканання.

Я думаю, што мовы тлумачальныя добрыя ў гэтым.

1
дададзена

C не мае якіх-небудзь метадаў (толькі функцыі), так што ваша пытанне не мае сэнсу ў C.

У 11 C ++, у здагадцы, што метад будзе зменены ў віртуальны , і мяркуецца, што ваш C ++ рэалізацыя выкарыстоўвае паказальнік віртуальных табліц, размешчаны ў пачатку аб'екта (гэта часта бывае з GCC на Linux), а калі і старыя і новыя класы маюць аднолькавы памер і выкарыстоўваюць адзінкавае ўспадкоўванне ад агульнай базавай класа (напрыклад, FooBase ), вы можаце выкарыстоўваць размяшчэнне новы аператара, таму ў асноўнай праграме:

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 на новы.

але <�моцны> гэта дрэнна пахне , верагодна, нявызначаны паводзіны </а> у адпаведнасці са стандартам C ++ 11, , але можа працаваць на некаторыя рэалізацыі C ++, і, вядома, залежыць ад канкрэтнай рэалізацыі. Я не рэкамендую кадавання такім чынам, нават калі гэта часам можа здарыцца "працаваць".

Ідыёматычны спосабам было б выкарыстоўваць паказальнікі на функцыі-члены або C ++ 11 засавак

.

Падобна на тое, ваша архітэктура убудова няправільна распрацавана. Паглядзіце на Qt убудовы для некаторага добрага натхнення.

1
дададзена

Таксама глядзіце на MSVC опцыю кампіляцыі/hotpatch. Гэта дазволіць стварыць код, дзе кожны ані-ўбудоўваецца спосаб пачынаецца з інструкцыяй, якая мае па меншай меры, 2 байта (кароткі JMP адносна 0 - г.зн. NOP). Затым вы можаце перапісаць вобраз вашага запушчанага прыкладання, і вы можаце захоўваць доўга JMP на вашу новую версію свайго метаду.

См Стварыць Hotpatchable малюнак.

Акрамя таго, напрыклад, на Linux у вас ёсць (даўно было) дзве функцыі называюцца «Еореп». Адзін з іх быў вызначаны на бібліятэцы Glibc, а другі быў вызначаны ў libpthread. Пазней адзін быў потокобезопасной. І калі вы dlopen-е выд libpthread, то «перамычка» на «FOPEN» функцыі перазапісвальны, а функцыя ад libpthread прывыкае.

Але гэта сапраўды залежыць ад вашай мэты.

1
дададзена
І /Ob0 , ўбудаванне выключаны, так што кожны метад можа быць hotpatched. Даунсайд: вектар :: аператар [] будзе гарачай patchable таксама, што, верагодна, не тое, што вы хацелі.
дададзена аўтар MSalters, крыніца
дададзена аўтар Lưu Vĩnh Phúc, крыніца

On Unix systems (e.g. Linux), dlsym is very useful for loading functions or entire libraries at runtime in C/C++. See e.g. http://www.tldp.org/HOWTO/C++-dlopen/thesolution.html

0
дададзена