Skip to content

Instantly share code, notes, and snippets.

@felipemanga
Last active May 17, 2022 15:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save felipemanga/f3fae94df8f5f9d07b3fd393fe93b2ee to your computer and use it in GitHub Desktop.
Save felipemanga/f3fae94df8f5f9d07b3fd393fe93b2ee to your computer and use it in GitHub Desktop.
Call a function in C++ with arguments in a vector
/*
Copyright (c) 2022 Felipe Manga
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
// Example use:
#include <iostream>
#include "ValueFunction.hpp"
int main() {
vf::Value::addBasicConverters(); // initialize converters
vf::Function func = [](int a, float b){return a + b;}; // assign like you'd do with std::function
std::string result = func({1, "4.5"}); // vf::Functions take a std::vector of vf::Values
std::cout << "str:[" << result << "]" << std::endl; // prints "str:[5.5000000]"
vf::Function vfun = [](int a){std::cout << a << std::endl;};
vfun.arguments.push_back("99.8");
vfun();
return 0;
}
*/
#pragma once
#include <any>
#include <cstddef>
#include <functional>
#include <string>
#include <tuple>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <vector>
namespace vf {
class Value {
struct converterHashFunc {
std::size_t operator () (const std::pair<std::type_index, std::type_index>& pair) const {
return pair.first.hash_code() * 31 + pair.second.hash_code();
}
};
using Converter = std::function<void(const std::any&, void*)>;
using ConverterMap = std::unordered_map<std::pair<std::type_index, std::type_index>, Converter, converterHashFunc>;
static ConverterMap& getConverters() {
static ConverterMap converters;
return converters;
}
template <typename T>
struct arg_type : public arg_type<decltype(&T::operator())> {};
template <typename ReturnType, typename ... Args>
struct arg_type<ReturnType(&)(Args...)> {
using type = std::remove_reference_t<decltype(std::get<0>(std::tuple<std::remove_reference_t<std::remove_cv_t<Args>>...>()))>;
};
template <typename ClassType, typename ReturnType, typename... Args>
struct arg_type<ReturnType(ClassType::*)(Args...) const> {
using type = std::remove_reference_t<decltype(std::get<0>(std::tuple<std::remove_reference_t<std::remove_cv_t<Args>>...>()))>;
};
std::any value;
public:
Value() = default;
template<typename Type>
Value(const Type& value) : value{value} {}
template<typename Type>
Value(Type&& value) : value{std::move(value)} {}
Value(const Value& other) : value{other.value} {}
Value(Value&& other) : value{std::move(other.value)} {}
template<typename Type>
Value& operator = (const Type& value) {this->value = value; return *this;}
template<typename Type>
Value& operator = (Type&& value) {this->value = std::move(value); return *this;}
Value& operator = (const Value& other) {this->value = other.value; return *this;}
Value& operator = (Value&& other) {this->value = std::move(other.value); return *this;}
template<typename Type>
operator Type () const {
auto key = std::make_pair(std::type_index(value.type()), std::type_index(typeid(Type)));
if (key.first == key.second)
return std::any_cast<Type>(value);
auto& converters = getConverters();
auto it = converters.find(key);
Type ret{};
if (it != converters.end())
it->second(value, &ret);
return ret;
}
bool empty() const {
return !value.has_value();
}
void reset() {
value.reset();
}
template<typename Type, bool exact = false>
bool has() const {
auto key = std::make_pair(std::type_index(value.type()), std::type_index(typeid(Type)));
if (key.first == key.second)
return true;
if (!exact) {
auto& converters = getConverters();
auto it = converters.find(key);
return it != converters.end();
}
return false;
}
template<typename Func, typename This = typename arg_type<Func>::type, typename That = std::result_of_t<Func(This)>>
static void addConverter(const Func& func) {
auto& converters = getConverters();
auto key = std::make_pair(std::type_index(typeid(This)), std::type_index(typeid(That)));
converters[key] = [func](const std::any& value, void* target) {
*reinterpret_cast<That*>(target) = func(std::any_cast<This>(value));
};
}
template<typename Type>
static void addIntConverters() {
addConverter([](Type v){return static_cast<float>(v);});
addConverter([](Type v){return static_cast<signed char>(v);});
addConverter([](Type v){return static_cast<bool>(v);});
addConverter([](Type v){return static_cast<char>(v);});
addConverter([](Type v){return static_cast<short>(v);});
addConverter([](Type v){return static_cast<int>(v);});
addConverter([](Type v){return static_cast<long>(v);});
addConverter([](Type v){return static_cast<long long>(v);});
addConverter([](Type v){return static_cast<double>(v);});
addConverter([](Type v){return static_cast<unsigned char>(v);});
addConverter([](Type v){return static_cast<unsigned short>(v);});
addConverter([](Type v){return static_cast<unsigned int>(v);});
addConverter([](Type v){return static_cast<unsigned long>(v);});
addConverter([](Type v){return static_cast<unsigned long long>(v);});
addConverter([](Type v){return std::to_string(v);});
addConverter([](const std::string& str){return static_cast<Type>(atoi(str.c_str()));});
addConverter([](const char* str){return static_cast<Type>(atoi(str));});
addConverter([](char* str){return static_cast<Type>(atoi(str));});
}
template<typename Type>
static void addFloatConverters() {
addConverter([](Type v){return static_cast<float>(v);});
addConverter([](Type v){return static_cast<double>(v);});
addConverter([](Type v){return v != 0;});
addConverter([](Type v){return static_cast<signed char>(v + Type(0.5));});
addConverter([](Type v){return static_cast<char>(v + Type(0.5));});
addConverter([](Type v){return static_cast<short>(v + Type(0.5));});
addConverter([](Type v){return static_cast<int>(v + Type(0.5));});
addConverter([](Type v){return static_cast<long>(v + Type(0.5));});
addConverter([](Type v){return static_cast<long long>(v + Type(0.5));});
addConverter([](Type v){return static_cast<unsigned char>(v + Type(0.5));});
addConverter([](Type v){return static_cast<unsigned short>(v + Type(0.5));});
addConverter([](Type v){return static_cast<unsigned int>(v + Type(0.5));});
addConverter([](Type v){return static_cast<unsigned long>(v + Type(0.5));});
addConverter([](Type v){return static_cast<unsigned long long>(v + Type(0.5));});
addConverter([](Type v){return std::to_string(v);});
addConverter([](const std::string& str){return static_cast<Type>(atof(str.c_str()));});
addConverter([](const char* str){return static_cast<Type>(atof(str));});
addConverter([](char* str){return static_cast<Type>(atof(str));});
}
static void addBasicConverters() {
addIntConverters<signed char>();
addIntConverters<char>();
addIntConverters<short>();
addIntConverters<int>();
addIntConverters<long>();
addIntConverters<long long>();
addIntConverters<double>();
addIntConverters<unsigned char>();
addIntConverters<unsigned short>();
addIntConverters<unsigned int>();
addIntConverters<unsigned long>();
addIntConverters<unsigned long long>();
addFloatConverters<float>();
addFloatConverters<double>();
addConverter([](bool v){return v ? "true" : "false";});
addConverter([](const char* v){return v && v[0] == 't';});
addConverter([](char* v){return v && v[0] == 't';});
addConverter([](const std::string& v){return v == "true";});
}
};
class Function {
private:
// For generic types, directly use the result of the signature of its 'operator()'
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())> {};
template <typename ReturnType, typename ... Args>
struct function_traits<ReturnType(&)(Args...)> {
enum { arity = sizeof...(Args) };
typedef ReturnType result_type;
template <typename Func, bool, typename Tuple, unsigned int ...I>
struct helper {
static Value call(Func&& func, Value* args){
return helper<Func, sizeof...(I) + 1 == std::tuple_size<Tuple>::value, Tuple, I..., sizeof...(I)>::call(func, args);
}
};
template <typename Func, unsigned int ...I>
struct helper<Func, true, std::tuple<Args...>, I...> {
static Value call(Func&& func, Value* args) {
return func(static_cast<Args>(args[I])...);
}
};
template <typename Func>
static Value call(Func&& func, Value * args) {
return helper<Func, sizeof...(Args) == 0, std::tuple<Args...>>::call(func, args);
}
};
// we specialize for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
enum { arity = sizeof...(Args) }; // arity is the number of arguments.
typedef ReturnType result_type;
template <typename Func, bool, typename Tuple, unsigned int ...I>
struct helper {
static Value call(Func&& func, Value* args){
return helper<Func, sizeof...(I) + 1 == std::tuple_size<Tuple>::value, Tuple, I..., sizeof...(I)>::call(func, args);
}
};
template <typename Func, unsigned int ...I>
struct helper<Func, true, std::tuple<Args...>, I...> {
static Value call(Func&& func, Value* args) {
if constexpr (std::is_void_v<ReturnType>) {
func(static_cast<Args>(args[I])...);
return {};
} else {
return func(static_cast<Args>(args[I])...);
}
}
};
template <typename Func>
static Value call(Func&& func, Value * args) {
return helper<Func, sizeof...(Args) == 0, std::tuple<Args...>>::call(func, args);
}
};
std::function<void(Value&, std::vector<Value>&)> call;
std::size_t argCount;
static inline std::vector<Value>** getVarArgsPtr() {
static std::vector<Value>* ptr = nullptr;
return &ptr;
}
public:
std::vector<Value> defaults;
std::vector<Value> arguments;
Value result;
static std::vector<Value>& varArgs() {
return **getVarArgsPtr();
}
Function() : call([](Value& ret, std::vector<Value>&){}), argCount(0) {}
Function(Function&& other) {
argCount = other.argCount;
call = other.call;
}
Function(const Function& other) = default;
Function(const Function&& other) : call(other.call), argCount(other.argCount) {}
Function& operator = (Function&& other) = default;
Function& operator = (const Function& other) = default;
template<typename NativeFunction>
Function(NativeFunction&& func) : argCount(function_traits<NativeFunction>::arity) {
call = [func](Value& result, std::vector<Value>& arguments) {
*getVarArgsPtr() = &arguments;
result = function_traits<NativeFunction>::call(func, arguments.data());
};
}
template<typename ... Arg>
void setDefault(Arg ... arg) {
defaults = {std::forward<Arg>(arg)...};
}
const Value& operator () () {
for (int i = 0, max = std::min(defaults.size(), argCount); i < max; ++i) {
arguments.push_back(defaults[i]);
}
while (arguments.size() < argCount) {
arguments.push_back(Value{});
}
call(result, arguments);
arguments.clear();
return result;
}
const Value& operator () (const std::vector<Value>& args) {
arguments = args;
(*this)();
return result;
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment