Skip to content

Instantly share code, notes, and snippets.

@RMDarth
Last active September 23, 2020 20:31
Show Gist options
  • Save RMDarth/1bda5c75dc9a23e55e8387d89ff34dcd to your computer and use it in GitHub Desktop.
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
// 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