[C++] A find-Function to Append .and_then(..).or_else(..) in C++
I'm afraid I haven't had the time to dive into C++23 yet. But today I came across a LinkedIn post by Šimon Tóth, in which he gave an example of std::optional
in C++23. In Short: C++23 introduced a monadic interface. This means that you can sort of append functions/lambdas. Just imagine a std::optional
of any type, you can do:
// any optional
std::optional<int> any_opt;
// ...
// i can call and_then on my optioanl and a
// lambda (or callable) is executed. if the optional
// doesn't hold a value, the lambda isn't executed
any_opt.and_then([](int& value){/*do something...*/});
There are more functions to use. But this got me thinking...
Lately I have been working a lot with containers and find
and find_if
functions. Most of the time I have an unordered_map
, but for the sake of simplicity I'll stick to a vector<int>
in this article.
In my particular case, the value I want to find is not always guaranteed to exist. This means that I often use code like this:
// any existing vector
std::vector<int> v = {1,2,3,4};
// lets use std::find
auto it = std::find(v.begin(), v.end(), any_value);
// and we'll see if we found our value
if (it == v.end()) {
// do this
} else {
// do that
}
I honestly think that it will be okay if I don't use it too often in my code, but messes do grow fast, don't they? And this is what brought me to the idea of using the C++23 std::optional
approach to find
.
Note that this is a very simplified approach, there is still a lot of work to be done to make this kind of overload work for all available containers. It's more for me to get my thoughts down on paper (or in this case, on the screen).
namespace cwt
{
// first we craete a type which will hold our find result
template<typename T>
class find_result
{
public:
find_result() = default;
find_result(T value) : m_value{value} {}
// this is the and_then method which gets a callback
template<typename Func>
const find_result<T>& and_then(const Func&& func) const
{
// we only call the callback if we have a value
if (m_value.has_value()) {
func(m_value.value());
}
// and to further add or_else we return *this
return *this;
}
// almost same here, just with return type void
template<typename Func>
void or_else(const Func&& func) const
{
if (!m_value.has_value()) {
func();
}
}
private:
// and since we don't know if we found a value
// we hold possible one as optional
std::optional<T> m_value{std::nullopt};
};
// this my find function, where try to find a value in a vector
template<typename T>
find_result<T> find(const std::vector<T>& v, const T value)
{
// like before we use the iterator
auto it = std::find(v.begin(), v.end(), value);
// and if we didn't find the value we return
// find_result default constructed
if (it == v.end()) {
return find_result<T>();
} else {
// or with the found value
return find_result<T>(*it);
}
}
} // namespace cwt
int main()
{
// lets create a simple vector of int values
std::vector<int> v = {1,2,3,4};
// we use our find function
// and since we return find_result<int>
// we can append or call and_then or_else directly
cwt::find(v, 2)
.and_then([](int result){ std::cout << "found " << result << '\n'; })
.or_else([](){ std::cout << "found nothing\n"; })
;
cwt::find(v, 10)
.and_then([](int result){ std::cout << "found " << result << '\n'; })
.or_else([](){ std::cout << "found nothing\n"; })
;
return 0;
}
Conclusion
Find this example here on compiler explorer.
I couldn't find anything existing about this or a simplified solution to find
. If somebody has another solution or knows something existing, please let me know and leave a comment.
I really liked the C++23 std::optional
, so these are just some initial thoughts. I'm working on a controller and I don't have C++23 available at the moment. So this would be a workaround for C++17/20. If I had C++23 then I could just return a std::optional
from find to have the same behavior.
I think this might make my code a little better in a couple of places. I know it's still a lot of work to have the full proofing solution for all containers, more accessors and so on and so forth. But it's an idea and maybe it will grow.
Have a nice day.
Best Thomas