I'm trying to find a [better] way to run/check a potentially unsafe expression or perform multiple null checks in a more elegant way.
Here is an example of codes I would like to improve:
if (myObjectPointer &&
myObjectPointer->getSubObject() &&
myObjectPointer->getSubObject()->getSubSubObject() &&
myObjectPointer->getSubObject()->getSubSubObject()->getTarget()) {
// Use safely target
... *(myObjectPointer->getSubObject()->getSubSubObject()->getTarget()) ...
}
I tried to find a more elegant way to achieve this (instead of the above verbose null checks). Here is my first thoughts:
template<typename T>
bool isSafe(T && function) {
try {
function();
// Just running the func above, but we could e.g. think about returning the actual value instead of true/fase - not that important.
return true;
}
catch (...) {
return false;
}
}
...
// And use the above as follow :
if(isSafe([&](){ myObjectPointer->getSubObject()->getSubSubObject()->getTarget(); })) {
// Use safely target
}
...
The problem with the above is that we can't catch signals (Segmentation fault, ...). And I obviously don't want to handle all signals in the program, but only in this very specific check/eval function.
I'm I tackling the problem the wrong way ? Any other recommendations ? or the verbose if is inevitable ?
Many thanks in advance.
I was thinking about this, and like Jarod42 said, there must be some variadic template stuff. I'm not the best at this, but came up with this:
#include <memory>
#include <functional>
#include <iostream>
template <typename T, typename MemFn, typename... Params>
void safeExecute(T* ptr, MemFn memFn, Params&&... params) {
if (ptr != nullptr)
safeExecute(std::invoke(memFn, ptr), std::forward<Params>(params)...);
}
template <typename T, typename MemFn>
void safeExecute(T* ptr, MemFn memFn) {
if (ptr != nullptr) std::invoke(memFn, ptr);
}
struct Target {
void Bar() { std::cout << "tada!\n"; };
};
template<typename T>
class Object {
private:
std::unique_ptr<T> ptr;
public:
Object() : ptr(std::make_unique<T>()) {}
T* Get() { return ptr.get(); }
};
using SubSubObject = Object<Target>;
using SubObject = Object<SubSubObject>;
using MyObject = Object<SubObject>;
int main() {
auto myObjectPtr = std::make_unique<MyObject>();
safeExecute(myObjectPtr.get(),
&MyObject::Get,
&SubObject::Get,
&SubSubObject::Get,
&Target::Bar);
}
edit:
I've been playing with the idea of having a more general return type, so I experimented with the option not to call the member function, but to return an std::optional pointer to the object. This lead me to the following code:
#include <memory>
#include <functional>
#include <iostream>
#include <optional>
template <typename T, typename MemFn, typename... Params>
auto safeGetObject(T* ptr, MemFn memFn, Params&&... params)
-> decltype(safeGetObject(std::invoke(memFn, std::declval<T>()), std::forward<Params>(params)...))
{
if (ptr != nullptr) return safeGetObject(std::invoke(memFn, ptr), std::forward<Params>(params)...);
return {};
}
template <typename T, typename MemFn>
auto safeGetObject(T* ptr, MemFn memFn) -> std::optional<decltype(std::invoke(memFn, std::declval<T>()))> {
if (ptr != nullptr) return std::invoke(memFn, ptr);
return {};
}
struct Target {
int Bar(int a, int b) const noexcept {
return a+b;
};
};
template<typename T>
class Object {
private:
std::unique_ptr<T> ptr;
public:
Object() noexcept : ptr(std::make_unique<T>()) {}
T* Get() const noexcept { return ptr.get(); }
};
using SubSubObject = Object<Target>;
using SubObject = Object<SubSubObject>;
using MyObject = Object<SubObject>;
int main() {
auto myObjectPtr = std::make_unique<MyObject>();
auto optionalTarget = safeGetObject(
myObjectPtr.get(),
&MyObject::Get,
&SubObject::Get,
&SubSubObject::Get);
auto result = optionalTarget ? optionalTarget.value()->Bar(3, 4) : -1;
std::cout << " result " << result << '\n';
}
Putting possible design issues aside, you could use an extended version of std::optional. Since not all intefaces are under your control, you would have to wrap the functions were necessary into a free-function. Let's assume you can change the class MyClass of myObjectPointer, but not the classes of the sub-objects.
class MyClass {
public:
optional<std::reference_wrapper<SubObjectClass>> getSubObject();
};
optional<std::reference_wrapper<SubSubObjectClass>> getSubSubObject(SubObjectClass& s) {
SubSubObjectClass* ptr = s.getSubSubObject();
if (ptr) {
return std::ref(s.getSubSubObject());
} else {
return {};
}
}
optional<std::reference_wrapper<Target>> getTarget(SubSubObjectCLass& s) {
...
}
You can now write something like
optional<MyClass*> myObjectPointer = ...;
myObjectPointer.and_then(MyClass::getSubObject)
.and_then(getSubSubObject)
.and_then(getTarget)
.map( doSomethingWithTarget ):
OK, I might delete my previous answer, because I've been rethinking this, now considering using std::optional and chaining.
Your original
myObjectPointer->getSubObject()->getSubSubObject()->getTarget()
is not really reproducible, since operator->() cannot be static. But we can use another operator, like operator>>(). Thus:
#include <memory>
#include <iostream>
#include <optional>
#include <functional>
struct Target {
int Bar(int a, int b) const noexcept { return a+b; };
};
template<typename T>
class Object {
private:
T* const ptr;
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using SubSubObject = Object<Target>;
using SubObject = Object<SubSubObject>;
using MyObject = Object<SubObject>;
template <typename T>
auto makeOptional(T* ptr) -> std::optional< std::reference_wrapper<T>> {
if (ptr) return std::ref(*ptr);
return {};
}
template <typename T, typename MemFn>
auto operator>> (std::optional<std::reference_wrapper<T>> optObj, MemFn memFn)
-> std::optional< std::reference_wrapper<std::remove_pointer_t<decltype(std::invoke(memFn, std::declval<T>()))>>> {
if (optObj) return makeOptional(std::invoke(memFn, *optObj));
return {};
}
int main() {
{
//complete
auto TargetPtr = std::make_unique<Target>();
auto subSubObjectPtr = std::make_unique<SubSubObject>(TargetPtr.get());
auto subObjectPtr = std::make_unique<SubObject>(subSubObjectPtr.get());
auto myObjectPtr = std::make_unique<MyObject>(subObjectPtr.get());
auto optionalMyObject = makeOptional(myObjectPtr.get());
auto optionalTarget = optionalMyObject >> &MyObject::Get >> &SubObject::Get >> &SubSubObject::Get;
auto result = (optionalTarget) ? optionalTarget->get().Bar(3, 4) : -1;
std::cout << "result is " << result << '\n';
}
{
// incomplete
auto subObjectPtr = std::make_unique<SubObject>(nullptr);
auto myObjectPtr = std::make_unique<MyObject>(subObjectPtr.get());
auto optionalMyObject = makeOptional(myObjectPtr.get());
auto optionalTarget = optionalMyObject >> &MyObject::Get >> &SubObject::Get >> &SubSubObject::Get;
auto result = (optionalTarget) ? optionalTarget->get().Bar(3, 4) : -1;
std::cout << "result is " << result << '\n';
}
}
will work...
Let me know if this is what you're looking for.
edit:
I've also tried putting it in a wrapper class
#include <memory>
#include <iostream>
#include <functional>
#include <optional>
struct Target {
constexpr int Bar(int a, int b) const noexcept { return a + b; };
};
template<typename T>
class Object {
private:
T* const ptr;
public:
constexpr Object(T* const ptr) noexcept : ptr(ptr) {}
constexpr T* Get() const noexcept { return ptr; }
};
using SubSubObject = Object<Target>;
using SubObject = Object<SubSubObject>;
using MyObject = Object<SubObject>;
template<typename T>
class ObjectWrapper {
private:
std::optional<std::reference_wrapper<T>> optRefObj{};
public:
constexpr ObjectWrapper(T* ptr) noexcept
: optRefObj(ptr ? std::make_optional(std::ref(*ptr)) : std::nullopt)
{}
template<typename MemFn>
constexpr auto operator>>(MemFn memFn) const noexcept {
return ObjectWrapper<std::remove_pointer_t<decltype(std::invoke(memFn, std::declval<T>()))>>
(optRefObj ? std::invoke(memFn, *optRefObj) : nullptr);
}
constexpr operator bool() const noexcept { return optRefObj.has_value(); }
constexpr T* Get() noexcept { return optRefObj ? &optRefObj->get() : nullptr; }
};
int main() {
{
//complete
auto const TargetPtr = std::make_unique<Target>();
auto const subSubObjectPtr = std::make_unique<SubSubObject>(TargetPtr.get());
auto const subObjectPtr = std::make_unique<SubObject>(subSubObjectPtr.get());
auto const myObjectPtr = std::make_unique<MyObject>(subObjectPtr.get());
auto const myObjWrp = ObjectWrapper(myObjectPtr.get());
auto optionalTarget = myObjWrp >> &MyObject::Get >> &SubObject::Get >> &SubSubObject::Get;
auto const result = optionalTarget ? optionalTarget.Get()->Bar(3, 4) : -1;
std::cout << "result is " << result << '\n';
}
{
// incomplete
auto const subObjectPtr = std::make_unique<SubObject>(nullptr);
auto const myObjectPtr = std::make_unique<MyObject>(subObjectPtr.get());
auto const myObjWrp = ObjectWrapper(myObjectPtr.get());
auto optionalTarget = myObjWrp >> &MyObject::Get >> &SubObject::Get >> &SubSubObject::Get;
auto const result = optionalTarget ? optionalTarget.Get()->Bar(3, 4) : -1;
std::cout << "result is " << result << '\n';
}
}
Related
I have a code that takes messages from flash player in a form of XML parse them into function and arguments and calls a registered callback for that function.
The piece of code that I want to replace is something nicely done (almost) generic Callback mechanism:
code for the generic callback implementation of flashSDK (ASInterface.inl).
The problem with it is that this code is written for flash and I want to replace the flash and use other service that will have the same interface. Is there any standard implementation of this callback mechanism (std? boost? something else open sourced?)?
This code implements generic callbacks mechanism that you can register function with number of arguments and types in a map:
void SomethingHappened(int a, int b) {print a + b;}
void SomethingElseHappened(string abcd) {print abcd;}
callbacks["SomethingHappened"] = &SomethingHappened;
callbacks["SomethingElseHappened"] = &SomethingElseHappened;
and than search for it and call with an array of arguments:
Callbacks::iterator itCallback = callbacks.find(functionName);
if (itCallback != callbacks.end())
{
HRESULT result = itCallback->second.Call(arguments, returnValue);
}
full usage example:
//init callbacks
std::map<std::wstring, Callback> callbacks;
void SomethingHappened(int a, int b) {print a + b;}
void SomethingElseHappened(string abcd) {print abcd;}
callbacks[functionName] = &SomethingHappened;
void MessageArrived(string xmlInput)
{
string functionName = parseFunctionName(xmlInput);
Callbacks::iterator itCallback = callbacks.find(functionName);
if (itCallback != callbacks.end())
{
//parse arguments
std::vector<std::wstring> args;
_Args::split(xml, args);
ASValue::Array arguments;
for (size_t i = 0, s = args.size(); i < s; ++i)
{
ASValue arg; arg.FromXML(args[i]);
arguments.push_back(arg);
}
ASValue returnValue;
//***this is where the magic happens: call the function***
HRESULT result = itCallback->second.Call(arguments, returnValue);
return result;
}
}
You probably need a wrapper around std::function, something like:
template <typename T> struct Tag{};
// Convert ASValue to expected type,
// Possibly throw for invalid arguments.
bool Convert(Tag<Bool>, AsValue val) { return (Boolean)val; }
int Convert(Tag<int>, AsValue val) { return (Number)val; }
// ...
struct Callback
{
private:
template <std::size_t ... Is, typename Ret, typename ... Ts>
static Ret call_impl(Ret(* func)(Ts...), std::index_sequence<Is...>)
{
if (arr.size() != sizeof...(Is)) throw std::invalid_argument{};
return func(Convert(tag<Ts>{}, arr[Is])...);
}
public:
template <typename Ret, typename ... Ts>
Callback(Ret(* func)(Ts...)) : Call{[func](ASValue::Array arr, ASValue& ret)
{
try
{
ret = Callback::call_impl(func, std::make_index_sequence<sizeof(...(Ts)>());
return S_OK;
} catch (...) {
return E_INVALIDARG;
}
}}
{}
std::function<HRESULT(ASValue::Array, ASValue&)> Call;
};
std::index_sequence is C++14, but you might find implementation on SO.
You could implement something like that.
A map of objects (GenericCallback here) containing std::function<R(Args...)> objects type-erased with std::any or std::variant.
You need to be careful in the way you call your function callbacks though.
E.g. I have to feed it a std::string("hello world") and not a simple C-string, otherwise the std::any_cast will throw (since a function<string(const char*)> is not a function<string(string)>).
#include <algorithm>
#include <any>
#include <functional>
#include <iostream>
#include <string>
#include <map>
#include <memory>
struct Caller {
virtual ~Caller() = default;
virtual std::any call(const std::vector<std::any>& args) = 0;
};
template<typename R, typename... A>
struct Caller_: Caller {
template <size_t... Is>
auto make_tuple_impl(const std::vector<std::any>& anyArgs, std::index_sequence<Is...> ) {
return std::make_tuple(std::any_cast<std::decay_t<decltype(std::get<Is>(args))>>(anyArgs.at(Is))...);
}
template <size_t N>
auto make_tuple(const std::vector<std::any>& anyArgs) {
return make_tuple_impl(anyArgs, std::make_index_sequence<N>{} );
}
std::any call(const std::vector<std::any>& anyArgs) override {
args = make_tuple<sizeof...(A)>(anyArgs);
ret = std::apply(func, args);
return {ret};
};
Caller_(std::function<R(A...)>& func_)
: func(func_)
{}
std::function<R(A...)>& func;
std::tuple<A...> args;
R ret;
};
struct GenericCallback {
template <class R, class... A>
GenericCallback& operator=(std::function<R(A...)>&& func_) {
func = std::move(func_);
caller = std::make_unique<Caller_<R, A...>>(std::any_cast<std::function<R(A...)>&>(func));
return *this;
}
template <class Func>
GenericCallback& operator=(Func&& func_) {
return *this = std::function(std::forward<Func>(func_));
}
std::any callAny(const std::vector<std::any>& args) {
return caller->call(args);
}
template <class R, class... Args>
R call(Args&&... args) {
auto& f = std::any_cast<std::function<R(Args...)>&>(func);
return f(std::forward<Args>(args)...);
}
std::any func;
std::unique_ptr<Caller> caller;
};
using namespace std;
//Global functions
int sub(int a, int b) { return a - b; }
std::function mul = [](int a, int b) { return a*b;};
std::string sortString(std::string str) {
std::sort(str.begin(), str.end());
return str;
}
int main()
{
std::map<std::string, GenericCallback> callbacks;
// Adding our callbacks
callbacks["add"] = [](int a, int b) { return a + b; };
callbacks["sub"] = sub;
callbacks["mul"] = std::move(mul);
callbacks["sortStr"] = sortString;
// Calling them (hardcoded params)
std::cout << callbacks["add"].call<int>(2, 3) << std::endl;
std::cout << callbacks["sub"].call<int>(4, 2) << std::endl;
std::cout << callbacks["mul"].call<int>(5, 6) << std::endl;
std::cout << callbacks["sortStr"].call<std::string>(std::string("hello world")) << std::endl;
// Calling "add" (vector of any params)
std::vector<std::any> args = { {1}, {2} };
std::any result = callbacks["add"].callAny(args);
std::cout << "result=" << std::any_cast<int>(result) << std::endl;
return 0;
}
https://godbolt.org/z/h63job
I am searching for something like swifts ? operator in c++ for std::function. I have grown to like it over the last couple years.
I would like a std::optional_function, which only calls the function if the function exists.
Something like this (but written by the gods of c++):
template<typename R>
struct option_function_result {
bool executed;
R result;
} ;
template<>
struct option_function_result<void>
{
bool executed;
} ;
template<typename F>
class optional_function
{
public:
typedef std::function<F> function_type;
typedef option_function_result<typename function_type::result_type> result_type;
protected:
function_type f;
public:
template<typename Fn>
optional_function operator=(const Fn &f_)
{
f = f_;
return *this;
}
template<typename Fn>
optional_function operator=(Fn &&f_)
{
f = std::forward<Fn>(f_);
return *this;
}
operator bool() const
{
return (bool)f;
}
template<typename ...Args, typename R>
result_type operator()(Args... args)
{
if (f)
return result_type { true, f(args...) };
return result_type { false };
}
template<typename ...Args>
result_type operator()(Args... args)
{
if (f)
{
f(args...);
return result_type { true };
}
return result_type { false };
}
} ;
Another revision
Here is revision 2. In order not to polute the question, and since I don't know if this will be a final answer, I'm gonna place it here for now:
I expect that the constructor for the struct is not necessary. However it forces the compiler to give me errors I need to debug the compilation.
template<typename R>
struct optional_function_result {
bool executed;
R result;
optional_function_result(bool &&executed_, R &&result_) :
executed (executed_),
result(result_) {}
} ;
template<>
struct optional_function_result<void>
{
bool executed;
optional_function_result(bool &&executed_) :
executed (executed_) {}
} ;
template<typename F>
class optional_function
{
public:
typedef std::function<F> function_type;
typedef typename std::function<F>::result_type function_result_type;
typedef optional_function_result<typename function_type::result_type> result_type;
protected:
function_type f;
public:
template<typename Fn>
optional_function operator=(const Fn &f_)
{
f = f_;
return *this;
}
template<typename Fn>
optional_function operator=(Fn &&f_)
{
f = std::forward<Fn>(f_);
return *this;
}
operator bool() const
{
return (bool)f;
}
template<
typename ... Args,
typename FR=function_result_type,
typename std::enable_if<!std::is_void<FR>::value, FR>::type* = nullptr
>
result_type operator()(Args... args) const
{
if (f)
return {
true,
std::forward<typename function_type::result_type>(f(args...))
};
return {
false,
function_result_type()
};
}
template<
typename ... Args,
typename FR=function_result_type,
typename std::enable_if<std::is_void<FR>::value, FR>::type* = nullptr
>
result_type operator()(Args... args) const
{
if (f)
{
f(args...);
return { true };
}
return { false };
}
} ;
Ok one more version, which uses basically optional to get rid of some edge cases.
template<typename T>
using optional_type = std::experimental::optional<T>;
template<typename R>
struct optional_function_result : optional_type<R> {
typedef optional_type<R> super_type;
optional_function_result() :
super_type() {}
optional_function_result(R &&result_) :
super_type(result_) {}
bool executed() const { return this->has_result(); }
} ;
template<>
struct optional_function_result<void>
{
bool executed_;
optional_function_result(bool &&executed__) :
executed_ (executed__) {}
bool executed() const { return executed_; }
} ;
template<typename F>
class optional_function
{
public:
typedef std::function<F> function_type;
typedef typename std::function<F>::result_type function_result_type;
typedef optional_function_result<typename function_type::result_type> result_type;
protected:
function_type f;
public:
template<typename Fn>
optional_function operator=(const Fn &f_)
{
f = f_;
return *this;
}
template<typename Fn>
optional_function operator=(Fn &&f_)
{
f = std::forward<Fn>(f_);
return *this;
}
operator bool() const
{
return (bool)f;
}
template<
typename ... Args,
typename FR=function_result_type,
typename std::enable_if<!std::is_void<FR>::value, FR>::type* = nullptr
>
result_type operator()(Args... args) const
{
if (f)
return {
std::forward<typename function_type::result_type>(f(args...))
};
return {};
}
template<
typename ... Args,
typename FR=function_result_type,
typename std::enable_if<std::is_void<FR>::value, FR>::type* = nullptr
>
result_type operator()(Args... args) const
{
if (f)
{
f(args...);
return { true };
}
return { false };
}
} ;
The ? operator works really well in C++ too:
// let function be of type std::function or a function pointer
auto var = f ? f() : default_value;
If you really want a type that does that, there is no such thing in the standard library, but a simple function is enough to do what you want (works only for function that don't return references or void):
template<typename F, typename... Args, typename R = std::invoke_result_t<F, Args&&...>>
auto optionally_call(F&& f, Args&&... args) -> std::optional<R> {
return f ? R(std::forward<F>(f)(std::forward<Args>(args)...)) : std::nullopt;
}
With some metaprogramming, it's possible to support cases not supported by this implementation.
This is to highlight that there's a lot of pitfalls when creating a whole type that is meant to be generic. There are many mistakes and performance issues and even code that will cannot be called in your sample code. A simple utility function would be easier than a type.
The standard library doesn't have anything like that, but you can build one yourself:
#include <functional>
#include <iostream>
#include <optional>
template <typename T>
class optional_function {
private:
std::optional<T> func;
public:
optional_function(T f) : func{std::move(f)} {}
optional_function() = default;
template <typename... Args>
auto operator()(Args&&... args) const {
using func_invoke_type = decltype((*func)(std::forward<Args>(args)...));
constexpr bool func_invoke_type_is_void = std::is_same_v<void, func_invoke_type>;
using optional_result_type = std::optional<
std::conditional_t<
func_invoke_type_is_void, // Can't have a std::optional<void>
char,
std::conditional_t<
std::is_reference_v<func_invoke_type>, // Can't have a std::optional<T&>
std::reference_wrapper<std::remove_reference_t<func_invoke_type>>,
func_invoke_type
>
>
>;
if (func) {
if constexpr (!func_invoke_type_is_void) {
return optional_result_type{(*func)(std::forward<Args>(args)...)};
} else {
(*func)(std::forward<Args>(args)...);
return optional_result_type{ '\0' }; // can't return void{} '
}
}
return optional_result_type{};
}
};
// Test it
void foo() {}
int main() {
optional_function f1{[](int i) { return i * i; }};
optional_function f2{[] { std::cout << "Hello World\n"; }};
decltype(f1) f3{};
optional_function f4{[](int a, const int& b) -> int const& {
std::cout << a + b << '\n';
return b;
}};
optional_function f5{foo};
auto res1 = f1(9);
auto res2 = f2();
auto res3 = f3(9);
int b = 5;
auto res4 = f4(1, b);
auto res5 = f5();
std::cout << std::boolalpha;
std::cout << "f1 is executed: " << res1.has_value() << ". result: " << *res1
<< '\n';
std::cout << "f2 is executed: " << res2.has_value() << '\n';
std::cout << "f3 is executed: " << res3.has_value() << '\n';
std::cout << "f4 is executed: " << res4.has_value() << ". result: " << *res4
<< '\n';
std::cout << "f5 is executed: " << res5.has_value() << '\n';
}
No, there is currently no such thing in the C++ Standard Library.
How to handle an api which returns different data types for the same input data types?
Looking at the below example, apicall should return a date or a string depending on the input attribute:
#include <iostream>
#include <string>
using namespace std;
???? apicall(string datatype, string attribute)
{
// code
}
int main(int argc, char** argv)
{
string datatype = "Thomas"
string attribute = "bithday"
cout << apicall(datatype, attribute) << endl;
string datatype = "Thomas"
string attribute = "address"
cout << apicall(datatype, attribute) << endl;
}
What could be in place of ???? (apicall return datatype) and how to handle these cases?
I am trying to understand these concepts as my experience to date has been with duck typed scripting languages.
The ideal solution is to use a std::variant, which is a safe union type like.
This allows you to write the following:
using DateOrString = std::variant<DateType, std::string>;
DateOrString api_call(std::string, std::string) {
// you can return both DateType and std::string
}
// ...
auto result = api_call("", "");
auto& str = std::get<std::string>(result);
Unfortunately std::variant is a C++17 feature. However different compilers already support it.
As already has been suggested, boost has a variant class and you can use it with any C++ standard.
As last option, you may implement a "variant-like" class which handles both a date and a string. Your function should return it.
Here a demo how to quickly implement that kind of class.
Note that that class is safe because the type is checked at runtime.
As a variant object, your callee function should branch on the type, something like:
auto result = api_call(/*...*/);
if (result.is_string()) {
// result is a string
const auto& str = result.get_string();
} else {
// result is a date
const auto& date = result.get_date();
}
... returns different data types for the same input data types?
This is literally impossible. A function is defined with one (or zero) return types, and zero or more input parameter types.
The workarounds are:
Write a single function returning a variant type, such as std::variant in C++17, or Boost.Variant if that's not available.
Write multiple functions with different return types (the caller just has to choose the right one)
Invert control, so that instead of returning a value, you pass an object capable of processing all the required types:
struct APIHandler {
virtual ~APIHandler() {}
virtual void operator()(int) {}
virtual void operator()(string) {}
};
void apicall(string name, string attr, APIHandler &h) {
// dummy implementation
if (attr == "address") {
h("123 Woodford Road");
} else if (attr == "birthday") {
h(19830214);
}
}
// implement your type-specific logic here
struct MyHandler: APIHandler {
void operator()(int i) override {
cout << "got an int:" << i << '\n';
}
void operator()(string s) override {
cout << "got a string:" << s << '\n';
}
};
// and use it like:
MyHandler mh;
apicall("Thomas", "birthday", mh);
apicall("Thomas", "address", mh);
You want a std::variant in C++17 or a boost::variant or roll your own crude variant something like this:
constexpr std::size_t max() { return 0; }
template<class...Ts>
constexpr std::size_t max( std::size_t t0, Ts...ts ) {
return (t0<max(ts...))?max(ts...):t0;
}
template<class T0, class...Ts>
struct index_of_in;
template<class T0, class...Ts>
struct index_of_in<T0, T0, Ts...>:std::integral_constant<std::size_t, 0> {};
template<class T0, class T1, class...Ts>
struct index_of_in<T0, T1, Ts...>:
std::integral_constant<std::size_t,
index_of_in<T0, Ts...>::value+1
>
{};
struct variant_vtable {
void(*dtor)(void*) = 0;
void(*copy)(void*, void const*) = 0;
void(*move)(void*, void*) = 0;
};
template<class T>
void populate_vtable( variant_vtable* vtable ) {
vtable->dtor = [](void* ptr){ static_cast<T*>(ptr)->~T(); };
vtable->copy = [](void* dest, void const* src){
::new(dest) T(*static_cast<T const*>(src));
};
vtable->move = [](void* dest, void* src){
::new(dest) T(std::move(*static_cast<T*>(src)));
};
}
template<class T>
variant_vtable make_vtable() {
variant_vtable r;
populate_vtable<T>(&r);
return r;
}
template<class T>
variant_vtable const* get_vtable() {
static const variant_vtable table = make_vtable<T>();
return &table;
}
template<class T0, class...Ts>
struct my_variant {
std::size_t index = -1;
variant_vtable const* vtable = 0;
static constexpr auto data_size = max(sizeof(T0),sizeof(Ts)...);
static constexpr auto data_align = max(alignof(T0),alignof(Ts)...);
template<class T>
static constexpr std::size_t index_of() {
return index_of_in<T, T0, Ts...>::value;
}
typename std::aligned_storage< data_size, data_align >::type data;
template<class T>
T* get() {
if (index_of<T>() == index)
return static_cast<T*>((void*)&data);
else
return nullptr;
}
template<class T>
T const* get() const {
return const_cast<my_variant*>(this)->get<T>();
}
template<class F, class R>
using applicator = R(*)(F&&, my_variant*);
template<class T, class F, class R>
static applicator<F, R> get_applicator() {
return [](F&& f, my_variant* ptr)->R {
return std::forward<F>(f)( *ptr->get<T>() );
};
}
template<class F, class R=typename std::result_of<F(T0&)>::type>
R visit( F&& f ) & {
if (index == (std::size_t)-1) throw std::invalid_argument("variant");
static const applicator<F, R> table[] = {
get_applicator<T0, F, R>(),
get_applicator<Ts, F, R>()...
};
return table[index]( std::forward<F>(f), this );
}
template<class F,
class R=typename std::result_of<F(T0 const&)>::type
>
R visit( F&& f ) const& {
return const_cast<my_variant*>(this)->visit(
[&f](auto const& v)->R
{
return std::forward<F>(f)(v);
}
);
}
template<class F,
class R=typename std::result_of<F(T0&&)>::type
>
R visit( F&& f ) && {
return visit( [&f](auto& v)->R {
return std::forward<F>(f)(std::move(v));
} );
}
explicit operator bool() const { return vtable; }
template<class T, class...Args>
void emplace( Args&&...args ) {
clear();
::new( (void*)&data ) T(std::forward<Args>(args)...);
index = index_of<T>();
vtable = get_vtable<T>();
}
void clear() {
if (!vtable) return;
vtable->dtor( &data );
index = -1;
vtable = nullptr;
}
~my_variant() { clear(); }
my_variant() {}
void copy_from( my_variant const& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->copy( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
void move_from( my_variant&& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->move( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
my_variant( my_variant const& o ) {
copy_from(o);
}
my_variant( my_variant && o ) {
move_from(std::move(o));
}
my_variant& operator=(my_variant const& o) {
copy_from(o);
return *this;
}
my_variant& operator=(my_variant&& o) {
move_from(std::move(o));
return *this;
}
template<class T,
typename std::enable_if<!std::is_same<typename std::decay<T>::type, my_variant>{}, int>::type =0
>
my_variant( T&& t ) {
emplace<typename std::decay<T>::type>(std::forward<T>(t));
}
};
then your code looks like:
variant<string, int> apicall(string datatype, string attribute)
{
if (datatype > attribute) return string("hello world");
return 7;
}
int main()
{
string datatype = "Thomas"
string attribute = "bithday"
apicall(datatype, attribute).visit([](auto&&r){
cout << r << endl;
});
string datatype = "Thomas"
string attribute = "address"
apicall(datatype, attribute).visit([](auto&& r){
cout << r << endl;
});
}
with whatever visit or apply_visitor free function or method your particular variant supports.
This gets much more annoying in C++11 as we don't have generic lambdas.
You could use a variant, but it's up to the caller site to check the results. Boost and std defines two variant types, i.e. std::variant and std::any.
class BaseClass{
public:
std::string name;
BaseClass(std::string typeName) : name(typeName) {};
std::string GetType(){ return name; }
};
template<typename T>
class DerivedClass : public BaseClass{
public:
T val;
DerivedClass(std::string typeName, T arg) : BaseClass(typeName), val(arg) {};
};
template<typename U, typename L>
void foo1(U & arg1, L & arg2)
{
std::cout << arg1.val + arg2.val << std::endl;
}
void foo(BaseClass *arg1, BaseClass *arg2)
{
if(arg1->GetType() == "Int")
{
auto p1 = (DerivedClass<int>*)arg1;
if(arg2->GetType() == "Int")
{
auto p2 = (DerivedClass<int>*)arg2;
foo1(*p1, *p2);
}
else if(arg2->GetType() == "Float")
{
auto p2 = (DerivedClass<float>*)arg2;
foo1(*p1, *p2);
}
//else if () AND SO ON ........
}
else if(arg1->GetType() == "Float")
{
auto p1 = (DerivedClass<float>*)arg1;
if(arg2->GetType() == "Int")
{
auto p2 = (DerivedClass<int>*)arg2;
foo1(*p1, *p2);
}
else if(arg2->GetType() == "Float")
{
auto p2 = (DerivedClass<float>*)arg2;
foo1(*p1, *p2);
}
}
//else if () AND SO ON .....
}
int main()
{
BaseClass *k1 = new DerivedClass<int>("Int", 2);
BaseClass *k2 = new DerivedClass<float>("Float", 4.32);
foo(k1, k2);
return 0;
}
I have some similar problem as in test case above.
In function foo, is there any more elegant way of parsing multiple types to ladder of ifs to run template function?
Ladder of if for 1 or 2 arguments is not that bad, but it goes (arguments count)^(types count);
You can use some template magic to generate the if...else chains for you. Firstly, write a generic compile-time iteration function:
template <typename TF, typename... Ts>
void for_each_arg(TF&& f, Ts&&... xs)
{
return (void)(int[]){(f(std::forward<Ts>(xs)), 0)...};
}
You will also need something that binds a type to a string:
template <typename T>
struct bound_type
{
using type = T;
std::string _name;
bound_type(std::string name) : _name{std::move(name)} { }
};
Then you can use it to check the types that you're interested in:
void foo(BaseClass *arg1, BaseClass *arg2)
{
const auto for_bound_types = [](auto&& f)
{
return for_each_arg(std::forward<decltype(f)>(f),
bound_type<int>{"Int"},
bound_type<float>{"Float"},
bound_type<double>{"Double"});
};
for_bound_types([&](const auto& t1)
{
if(arg1->GetType() != t1._name) return;
for_bound_types([&](const auto& t2)
{
if(arg2->GetType() != t2._name) return;
using t1_type = typename std::decay_t<decltype(t1)>::type;
using t2_type = typename std::decay_t<decltype(t2)>::type;
auto& p1 = static_cast<DerivedClass<t1_type>&>(*arg1);
auto& p2 = static_cast<DerivedClass<t2_type>&>(*arg2);
foo1(p1, p2);
});
});
}
live wandbox example
I would like to avoid using std::string here, but compile-time strings are unreasonably hard in C++. Something like typestring could be used instead.
In C++17, for_each_arg would be redundant thanks to fold expressions. std::apply could also be used to implement for_bound_types.
const auto for_bound_types = [](auto&& f)
{
return std::apply([&](auto... xs){ (f(xs), ...); },
std::make_tuple(bound_type<int>{"Int"},
bound_type<float>{"Float"},
bound_type<double>{"Double"}));
};
The code generated will be args^types, but you can get the compiler to do it for you.
Get ahold of std::variant or boost::variant or write it yourself.
Have each argument give you either a variant with a copy, or a variant with a pointer-to the element or itself.
Use std::visit or boost::apply_visitor on these variants.
template<typename T>
class DerivedClass;
class BaseClass{
public:
std::string name;
virtual std::variant< DerivedClass<int>*, DerivedClass<double>* >
self() = 0;
BaseClass(std::string typeName) : name(typeName) {};
std::string GetType(){ return name; }
};
template<typename T>
class DerivedClass : public BaseClass{
public:
T val;
DerivedClass(std::string typeName, T arg) : BaseClass(typeName), val(arg) {};
std::variant< DerivedClass<int>*, DerivedClass<double>* >
self() overload { return this; }
std::variant< DerivedClass<int> const*, DerivedClass<double> const* >
self() const overload { return this; }
};
Now we get:
void foo(BaseClass *arg1, BaseClass *arg2)
{
auto a1 = arg1->self();
auto a2 = arg2->self();
auto foo_overloads = [](auto&&...args)->decltype(auto){ return foo(decltype(args)(args)...); };
std::visit( foo_overloads, a1, a2 );
}
Now this punts the problem to "how do I write std::visit and std::variant". But code for both is available on the internet. And both are available as std::experimental::variant and std::experimental::visit.
Maintaining the list of types int double can be done in a type list, and the list variant<DerivedClass<int>*, DerivedClass<double>*> generated from it.
If you want the list to be bespoke and dynamic at runtime cost, you could instead take a type list, build a map from std::string to std::function<variant< DerivedClass<Ts>*... >( BaseClass* )> at the point of use (listing the types you support there), and do the same thing there.
I'd like to make the return value type generic using std::function, but it does not work, code:
debuggable code can be found: http://cpp.sh/5bk5
class Test
{
public:
template <typename R, typename F = std::function<R()>>
R f(F&& op) {
op();
}
void t() {
int a = 10;
f([this, a]() { return "chars"; });
}
};
int main()
{
t::Test test;
test.t();
return 0;
}
You could avoid the Template/std::function way and use auto for return type.
If you can compile C++14 it's easy
// Example program
#include <iostream>
class Test
{
public:
Test(){ }
template <typename F>
auto f (F && op)
{ return op(); }
void t()
{ std::cout << f([this]() { return "chars"; }) << std::endl; }
};
int main()
{
Test test;
test.t();
return 0;
}
If you can compile only C++11 you have to use decltype() for Test::f()
template <typename F>
auto f (F && op) -> decltype( op() )
{ return op(); }