Skip to content

Instantly share code, notes, and snippets.

@pnck
Last active July 23, 2020 07:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pnck/5874d2a20368113fe6de03c02137768c to your computer and use it in GitHub Desktop.
Save pnck/5874d2a20368113fe6de03c02137768c to your computer and use it in GitHub Desktop.
// 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