Ответ 1
В зависимости от того, чего вы надеетесь достичь в результате этого, вы можете сделать что-то (достаточно легко для бесплатных функций или статических функций-членов) на С++ с объектами-функторами, которые переносят реальные вызовы, например:
#include <iostream>
template<void f(void)>
struct Wrap {
void operator()() const {
std::cout << "Pre call hook" << std::endl;
f();
}
};
namespace {
void test_func() {
std::cout << "Real function" << std::endl;
}
}
const Wrap<&test_func> wrapped_test_func = {};
int main() {
wrapped_test_func();
return 0;
}
Очевидно, что для этого требуется еще большая работа, чтобы быть достаточно общим, например. С++ 0x вариационных шаблонов или много перегрузок. Сделать его работу с функциями-членами также более сложно.
Я набросал схему для (неинтрузивного) способа сделать это для функций-членов тоже:
#include <iostream>
template<class C, void (C::*F)()>
class WrapMem {
C& inst;
public:
WrapMem(C& inst) : inst(inst) {}
void operator()() {
std::cout << "Pre (method) call hook" << std::endl;
((inst).*(F))();
}
void operator()() const {
std::cout << "Pre (method, const) call hook" << std::endl;
((inst).*(F))();
}
};
class Foo {
public:
void method() { std::cout << "Method called" << std::endl; }
void otherstuff() {}
};
class FooWrapped : private Foo {
public:
FooWrapped() : method(*this) {}
using Foo::otherstuff;
WrapMem<Foo,&Foo::method> method;
};
int main() {
FooWrapped f;
f.otherstuff();
f.method();
return 0;
}
Вы также можете пропустить частное наследование и using
, чтобы разоблачить не-обернутые методы, но вам нужно быть осторожным в отношении деструкторов, и это легко случайно обойти, если вы это сделаете. (например, неявное преобразование для ссылки на базу). Неинтрузивный способ также ограничивается работой только для открытого интерфейса, а не для внутренних вызовов.
С С++ 11 вы можете получить совершенную переадресацию, а также уменьшить конструкцию оберточных объектов до простого макроса, который принимает имя функции класса и члена и выводит остальное для вас, например:
#include <iostream>
#include <utility>
template <typename Ret, typename ...Args>
struct Wrapper {
template <class C, Ret (C::*F)(Args...)>
class MemberFn {
C& inst;
public:
MemberFn(C& inst) : inst(inst) {}
MemberFn& operator=(const MemberFn&) = delete;
Ret operator()(Args&& ...args) {
return ((inst).*(F))(std::forward<Args>(args)...);
}
Ret operator()(Args&& ...args) const {
return ((inst).*(F))(std::forward<Args>(args)...);
}
};
};
template <typename T>
struct deduce_memfn;
template <typename C, typename R, typename... Args>
struct deduce_memfn<R (C::*)(Args...)> {
template <R(C::*F)(Args...)>
static typename Wrapper<R, Args...>::template MemberFn<C, F> make();
};
template <typename T>
decltype(deduce_memfn<T>()) deduce(T);
template <typename T>
struct workaround : T {}; // Clang 3.0 doesn't let me write decltype(deduce(&Class::Method))::make...
#define WRAP_MEMBER_FN(Class, Method) decltype(workaround<decltype(deduce(&Class::Method))>::make<&Class::Method>()) Method = *this
class Foo {
public:
Foo(int);
double method(int& v) { return -(v -= 100) * 10.2; }
void otherstuff();
};
class WrappedFoo : private Foo {
public:
using Foo::Foo; // Delegate the constructor (C++11)
WRAP_MEMBER_FN(Foo, method);
using Foo::otherstuff;
};
int main() {
WrappedFoo f(0);
int i = 101;
std::cout << f.method(i) << "\n";
std::cout << i << "\n";
}
(Примечание: этот вывод не будет работать с перегрузками) Это было протестировано с помощью Clang 3.0.