Last active
July 23, 2020 07:00
-
-
Save pnck/5874d2a20368113fe6de03c02137768c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// only works on linux | |
// c++17 standard required | |
// g++-8 -g -std=c++17 -Wl,-E foo.cpp -ldl | |
#include <any> | |
#include <cxxabi.h> | |
#include <dlfcn.h> | |
#include <functional> | |
#include <iostream> | |
#include <map> | |
#include <memory> | |
#include <sstream> | |
#include <string> | |
#include <typeinfo> | |
#include <vector> | |
class DynamicAttributeClass { | |
private: | |
static constexpr auto get_fname = [](auto *fp) -> std::string { | |
Dl_info info; | |
dladdr(reinterpret_cast<void *>(fp), &info); | |
std::unique_ptr<char, void (*)(void *)> p{abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, 0), free}; | |
return std::string(p.get()); | |
}; | |
class _invoker { | |
friend class DynamicAttributeClass; | |
std::any fn_; | |
_invoker(std::any &&v) : fn_(std::move(v)) {} | |
void operator=(_invoker &&rv) { fn_ = std::move(rv.fn_); } | |
public: | |
_invoker() = default; | |
template <typename F, typename... ARGS> | |
auto invoke(ARGS &&... args) -> decltype(std::declval<F>()(args...)) { | |
using R = decltype(std::declval<F>()(args...)); | |
return std::any_cast<std::function<R(ARGS...)>>(fn_)(std::forward<ARGS>(args)...); | |
} | |
template <typename F> | |
explicit operator std::function<F>() { | |
return std::any_cast<std::function<F>>(fn_); | |
} | |
}; | |
class _attr { | |
friend class DynamicAttributeClass; | |
DynamicAttributeClass *_this; | |
_attr(DynamicAttributeClass *p) : _this(p) {} | |
public: | |
_invoker &operator[](std::string fname) { return _this->functions_[fname]; } | |
template <typename F> | |
auto get(std::string fname) -> std::function<F> { | |
return static_cast<std::function<F>>(_this->functions_[fname]); | |
} | |
}; | |
private: | |
std::map<std::string, _invoker> functions_; | |
public: | |
_attr attributes; | |
DynamicAttributeClass() : attributes(this) {} // constructor | |
public: | |
std::vector<std::string> list_attr() { | |
std::vector<std::string> ret; | |
for (auto &p : functions_) { | |
ret.emplace_back(p.first); | |
} | |
return ret; | |
} | |
template <typename R, typename... ARGS> | |
void add_func(R (&f)(ARGS...)) { // raw function | |
auto fname = get_fname(f); | |
functions_[fname.substr(0, fname.find_first_of('('))] = _invoker(std::make_any<std::function<R(ARGS...)>>(f)); | |
} | |
template <typename F, typename = std::conditional_t<std::is_class_v<F>, std::decay_t<decltype(&F::operator())>, | |
std::enable_if_t<std::is_class_v<F>>>> | |
void add_func(F &&f) { // any callable object | |
std::unique_ptr<char, void (*)(void *)> p{abi::__cxa_demangle(typeid(f).name(), nullptr, nullptr, 0), free}; | |
auto fname = std::string(p.get()); | |
auto _f = std::function{std::forward<F>(f)}; | |
functions_[fname] = _invoker(std::make_any<decltype(_f)>(std::move(_f))); | |
} | |
template <typename T, typename... ANY> | |
void add_func(T &&, ANY &&...) { // any invalid arguments | |
static_assert(std::is_same_v<T, std::false_type>, "Not a invocable object"); // always false | |
} | |
}; | |
namespace test { | |
void fn() { puts("fn called"); } | |
class A : public DynamicAttributeClass { | |
public: | |
void printA() { puts("imA"); } | |
}; | |
} // namespace test | |
int plus2(int a, int b) { return a + b; } | |
int main() { | |
test::A a; | |
a.add_func(test::fn); | |
a.add_func(plus2); | |
a.add_func([&] { a.printA(); }); | |
a.add_func([&a](int n) { | |
std::stringstream ss; | |
ss << n; | |
return ss.str(); | |
}); | |
// a.add_func(1); // foo.cpp:87:23: error: static assertion failed: Not a invocable object | |
// a.add_func(a); // foo.cpp:87:23: error: static assertion failed: Not a invocable object | |
for (auto s : a.list_attr()) { | |
std::cout << s << std::endl; // check attributes | |
} | |
endl(std::cout); | |
std::cout << "1+2=" << a.attributes["plus2"].invoke<int(int, int)>(1, 2) | |
<< std::endl; // call trivial function `plus2` by name | |
a.attributes["test::fn"].invoke<void()>(); // call trivial function `fn` by name | |
a.attributes["main::{lambda()#1}"].invoke<void()>(); // call closure object by full-qualified name | |
auto to_str = a.attributes.get<std::string(int)>("main::{lambda(int)#2}"); // get function object by name | |
std::cout << to_str(1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment