I'm not sure if the question name really reflects what I'm asking here, so please let me know if there are better titles.
I have a class that represents a value, which can be initialized either through a callable, or more cleanly through a reference to the variable.
template <typename F> concept not_callable = !std::invocable<F>;
template <typename... Args>
struct Values{
std::tuple<std::function<Args()>...> values;
Values(std::invocable auto&&... func): values{std::make_tuple(std::function(func)...)} {}
Values(not_callable auto&... args): values{std::make_tuple(std::function([&args](){return args;})...)} {}
};
Values(std::invocable auto&&... func) -> Values<std::remove_cvref_t<decltype(func())>...>;
Values(not_callable auto&... arg) -> Values<std::remove_cvref_t<decltype(arg)>...>;
This allows me to construct the function through either a series of functions, or references. However, I want to be able to initialize it with a mix of these.
int x;
char* y;
Values v1 (rand, clock);
Values v2 (x, y);
// Values v3 (x, rand); Doesn't work
Note that right now it fails because I have conflicting concepts for the constructors that won't allow both to be initialized together. However without the concepts, they don't compile at all. Is there a way to make this work?
An alternate solution is to have this class contain a tuple of another class, where that class is constructible from a single var or callable.
Unfortunately that would require syntax like the following with explicit casting for each parameter.
class singleV;
Value v4(singleV(x), singleV(rand));
Is there a way to be able to initialize it like v3
?
You can define a helper function to construct the corresponding std::function
object according to the type of the argument
template <typename T>
concept callable_or_variable =
std::invocable<T> || std::is_lvalue_reference_v<T>;
template <callable_or_variable T>
auto to_function(T&& t) {
if constexpr (std::invocable<T>)
return std::function<std::remove_cvref_t<std::invoke_result_t<T>>()>(t);
else
return std::function<std::remove_cvref_t<T>()>([&t] { return t; });
}
Then implement CTAD according to the return type of the std::function
type returned by this helper function
template <typename... Args>
struct Values{
std::tuple<std::function<Args()>...> values;
Values(callable_or_variable auto&&... args):
values{to_function(std::forward<decltype(args)>(args))...} {}
};
template <callable_or_variable... Args>
Values(Args&&...) ->
Values<std::invoke_result_t<decltype(to_function(std::declval<Args>()))>...>;