Background
I am writing a library that takes some data from the user and works with it. I was experimenting with ways to allow users to provide the data by the following methods:
- As a (global) variable (taken by a
const &
) - As a function pointer
- As a lambda (taken by value)
I am aware that I can just wrap the variable or function in a lambda to achieve the same result, but I think I can learn something by doing it the "hard" way.
I am targeting C++20.
Code
#include <cassert>
#include <cstdint>
#include <functional>
template <class T, bool = std::is_invocable_v<T>>
struct GetDataType
{
using type = typename std::invoke_result_t<T>;
};
template <class T>
struct GetDataType<T, false>
{
using type = T;
};
template <class T, bool = std::is_invocable_v<T>>
struct GetSourceType
{
using type = T;
};
template <class T>
struct GetSourceType<T, false>
{
using type = typename std::add_lvalue_reference_t<std::add_const_t<T>>;
};
template <typename DataSource>
class DataGetter
{
public:
using DataType = GetDataType<DataSource>::type;
using DataSourceType = GetSourceType<DataSource>::type;
DataGetter(const DataSource &f) : m_dataSource(f) {}
[[nodiscard]] bool dataMatches(DataType refData) const
{
return refData == getData();
}
private:
DataSourceType m_dataSource;
[[nodiscard]] DataType getData() const
{
if constexpr (std::invocable<DataSource>)
{
return std::invoke(m_dataSource);
}
else
{
return m_dataSource;
}
}
};
volatile uint32_t g_Data = 3;
uint32_t getData() { return g_Data + 1; }
int main(int argc, char **)
{
DataGetter fromLocal(argc);
DataGetter fromGlobal(g_Data);
DataGetter fromLambda([&argc]()
{ return argc + 1; });
DataGetter fromFunctionPointer(&getData);
assert(fromLocal.dataMatches(1));
assert(fromGlobal.dataMatches(3));
assert(fromLambda.dataMatches(2));
assert(fromFunctionPointer.dataMatches(4));
argc++;
g_Data = g_Data + 1;
assert(fromLocal.dataMatches(2));
assert(fromGlobal.dataMatches(4));
assert(fromLambda.dataMatches(3));
assert(fromFunctionPointer.dataMatches(5));
return 0;
}