Following peace of code is not working:
int main()
{
printf("Hello World\n");
std::function<int()> dummy1 = []() { return -3; };
std::function<unsigned()> dummy2 = []() { return 33333; };
auto test1 = std::make_shared<std::function<int()>>(dummy1);
auto test2 = std::make_shared<std::function<unsigned()>>(dummy2);
std::cout << "Final 1: " << (*test1)() << "\n";
std::cout << "Final 2: " << (*test2)() << "\n";
std::vector<std::shared_ptr<void>> ptr;
ptr.push_back(test1);
ptr.push_back(test2);
// Does not work
std::cout << "Final 3: " << (*ptr[0])() << "\n";
return 0;
}
https://onlinegdb.com/vJZ2q9ctk
How to define a std::vector to lambdas with different return values?
The bigger picture
The plan is to have in a concrete implementation a central data structure (for monitoring) which queries different getters in different modules with different return types.
The querying of the data shall take place on a regular base calling all the getters.
It should be possible to configure the getters at compile time.
To maintain a generic approach and only rely on a config.h file the vector should be formed as generic as possible (but avoiding std::any because of this seems a bit dubious for me)
How to define a std::vector to lambdas with different return values?
Impossible to implement in a simple useful way.
Imagine it is possible. Then I add the following two lambdas into your vector:
struct Foo {};
struct Bar {};
ptr.push_back([]() { return Foo{}; });
ptr.push_back([]() { return Bar{}; });
You cannot write code that uses ptr
that works both for struct Foo
and struct Bar
unless you assume something about them. Hence, you are actually not talking about arbitrary types. You talk about some restricted subset of types.
Maybe you want types that can be <<
ed. Maybe you have some list of types that can happen in your config (e.g. only std::string
, int
and double
) and depending on the config key the expected type is different.
But the compiler has no way of knowing that. Moreover, you cannot prove to the compiler that the return type depends on the key, it's called "dependent types" and is more or less non-existent in C++ unless you do template metaprogramming and compile-time evaluation shenanigans. The compiler has to generate code that works for all possible types that can happen. All elements of std::vector
have the same time in compile time. Hence, if you call them, the returned type is the same. But you can then do some run-time calculations to convert that returned type to some specific type of your choice.
In your specific case, I suggest std::variant<int, unsigned>
. Add all other types that can happen in the config.
#include <cstdio>
#include <functional>
#include <iostream>
#include <memory>
#include <variant>
int main()
{
printf("Hello World\n");
std::function<int()> dummy1 = []() { return -3; };
std::function<unsigned()> dummy2 = []() { return 33333; };
auto test1 = std::make_shared<std::function<std::variant<int, unsigned>()>>(dummy1);
auto test2 = std::make_shared<std::function<std::variant<int, unsigned>()>>(dummy2);
std::cout << "Final 1: " << std::get<int>((*test1)()) << "\n";
std::cout << "Final 2: " << std::get<unsigned>((*test2)()) << "\n";
std::vector<std::shared_ptr<std::function<std::variant<int, unsigned>()>>> ptr;
ptr.push_back(test1);
ptr.push_back(test2);
std::cout << "Final 3: " << std::get<int>((*ptr[0])()) << "\n";
return 0;
}
Note that you should know in advance what is the returned type of each element of vector. If you guess incorrectly, std::get
throws std::bad_variant_access
. Or you can ask via .index()
or pass a generic lambda via .visit()
.