可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want to make a plugin system with the capability to override a method at runtime.
Some answers say function pointers, but how about a defined function or class?
Like this:
class foo
{
public:
bar(int foobar);
}
Is there a way to get function pointer for that, or replace it?
BTW, hooking is not considered an answer because it's very platform specific and dangerous.
回答1:
Runtime function "replacement" can be achieved with one of several techniques:
- Polymorphism
- Standard library facilities such as
std::function
- Third party libraries or platform specific techniques to translate or dispatch function calls.
On which option is better is highly dependent on the intended use and target environments. For example; plugin systems could well make use of polymorphism (with the appropriate factories and possibly even combined with the template method pattern) whilst internal function routing could use std::function
.
These techniques wouldn't really "replace" any of the functions, but rather can be set up at runtime to route the function call as required.
Note I've focused on the C++ aspects of the question (it was tagged C and C++ but the sample code is C++).
回答2:
To make a plugin system you don't need to replace a method of a class at runtime.
You can replace what the method does by using polymorphism or any other way to configure an object.
Check out the answers to the following question: What's safe for a C++ plug-in system?
回答3:
While you can't directly replace a method, this can be solved with another layer of indirection.
#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);
}
Currently this replaces the method of the instance. If you want to replace the method of a class, make the_function
static (even better, make it a static method returning a static variable to avoid static initialization order fiasco)
回答4:
Back to the original question: "Is it possible to replace a method at runtime in C/C++" it is possible and there are some use-cases for it, yet (as others have said) most of these use cases don't apply to you. This is also not very straightforward.
For example linux kernell can use something called kpatch or kGraft. This is quite complicated mechanism --- which is of course not very portable, and not very usable in userspace programs, as these techniques rely on mechanisms baked into linux kernell.
回答5:
C does not have any methods (only functions), so your question is meaningless in C.
In C++11, assuming the method to be changed is virtual
, and assuming your C++ implementation is using a vtable pointer located at start of the object (this is often the case with GCC on Linux), and if both old and new classes have the same size and are using single inheritance from a common base class (e.g. FooBase
), you could use the placement new
operator, so in your main program:
class FooBase {
virtual ~FooBase();
virtual int bar(int);
/// etc
}
class ProgramFoo : public FooBase {
virtual ~ProgramFoo();
virtual int bar (int);
/// other fields and methods
};
and in your plugin:
class PluginFoo : public FooBase {
virtual ~ProgramFoo();
virtual int bar (int);
/// other fields and methods
static_assert(sizeof(PluginFoo) == sizeof(ProgramFoo),
"invalid PluginFoo size");
};
then you might have some plugin function like
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
}
it hopefully would override the old vptr by the new one.
but this smells bad, is probably undefined behavior according to the C++11 standard, but might work on some C++ implementations, and is certainly implementation specific. I don't recommend coding that way, even if it might sometimes happen to "work".
The idiomatic way would be to use member function pointers or C++11 closures.
It looks like your plugin architecture is wrongly designed. Look into Qt plugins for some good inspiration.
回答6:
also look at MSVC compile option /hotpatch. This will create a code, where each non-inlined method starts with an instruction having at least 2 bytes (short jmp relative 0 - i.e. NOP). Then you can rewrite image of your running application and you can store long jmp onto your new version of your method.
See Create Hotpatchable Image.
Also for example on Linux you have(long time ago had) two function called "fopen". One of them was defined on library glibc and the other one was defined in libpthread. The later one was thread-safe. And when you dlopen-ed libpthread, the "jumper" onto "fopen" function gets overwritten, and a function from libpthread gets used.
But it really depends on your goal.
回答7:
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
回答8:
I guess it is not possible for C/C++.
The function code has been compiled to binary, you cannot change it dunning run-time.
I think interpretative languages are good at it.