Last active
September 23, 2020 20:31
-
-
Save RMDarth/1bda5c75dc9a23e55e8387d89ff34dcd to your computer and use it in GitHub Desktop.
Visit template class and invoke its method by generic lambda, with default function call when method doesn't exist
This file contains hidden or 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
// I had an issue when there are a lot of classes (or structures), which are not related to each other, | |
// yet most of them have fields or methods with the same name/signature - for example getName(), or getOperationCode(). | |
// I wanted a simple method, like GetParam(my_object, GetName(), "NoName"), which will return name if the provided method | |
// is present, and return default value if it's not. Codebase is in C++14. | |
// To achieve this, we can use 2 template methods - one, which will try to get given param, and second with default behaviour. | |
// First method could use generic lambda as a parameter to access the param. But SFINAE doesn't work with such lambda, so | |
// lambda, too, must use additional enabler for itself, so it won't be instantiated for class which doesn't have given param. | |
// | |
// Here is simplified solution for the described issue. To simplify creating the lambda with all the checks, I've added a | |
// macro "invoke", which will only need the object, default value and expression to access the parameter. | |
#include <iostream> | |
class Foo | |
{ | |
public: | |
std::string getName() const | |
{ | |
return "Name: Foo"; | |
} | |
}; | |
class Bar | |
{ | |
public: | |
std::string getContent() const | |
{ | |
return "Content: 123456"; | |
} | |
}; | |
class FooBar | |
{ | |
public: | |
std::string getName() const | |
{ | |
return "Name: FooBar"; | |
} | |
std::string getContent() const | |
{ | |
return "Content: 3.14159265"; | |
} | |
}; | |
template <typename Type, typename Pred> | |
auto invoke_impl(const Type& type, const std::string& /*default_val*/, const Pred& pred) -> decltype(pred(type), std::string()) | |
{ | |
return pred(type); | |
} | |
template<typename Type, typename ...Args> | |
auto invoke_impl(const Type& /*type*/, const std::string& default_val, ...) -> std::string | |
{ | |
return default_val; | |
} | |
#define invoke(param, default_val, operation) \ | |
invoke_impl(param, default_val, [](const auto& type) -> decltype(operation, std::string()) { return operation; }) | |
// Try accessing parameters | |
template <typename T> | |
void GetNameAndContent(const T& t) | |
{ | |
std::cout << invoke(t, "Name: Not found", type.getName()) << std::endl; | |
std::cout << invoke(t, "Content: Not found", type.getContent()) << std::endl; | |
std::cout << std::endl; | |
} | |
int main() | |
{ | |
Foo foo = {}; | |
GetNameAndContent(foo); | |
Bar bar = {}; | |
GetNameAndContent(bar); | |
FooBar foobar = {}; | |
GetNameAndContent(foobar); | |
return 0; | |
} | |
/* Output: | |
Name: Foo | |
Content: Not found | |
Name: Not found | |
Content: 123456 | |
Name: FooBar | |
Content: 3.14159265 */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment