I need a class with a method that returns some kind of range using the range-v3 library. In order to implement such a class I can write it everything right in the definition of that class. For example:
#include <iostream>
#include <set>
#include <range/v3/view/transform.hpp>
class Alpha {
public:
int x;
};
class Beta : public Alpha {};
class Foo {
public:
std::set<Alpha*> s;
auto r() { return s | ranges::v3::view::transform([](Alpha* a) { return static_cast<Beta*>(a); }) }
};
However, in my real case the Foo::r
function is quite complex and I would like to hide its implementation.
In particular, the implementation make uses of some additional libraries that otherwise do not need to be included when the class Foo
is declared.
However, when the definition of Foo::r
is separated from its declaration, its return type must be specified explicitly. decltype
comes with some help:
Header file(s):
class Foo {
public:
std::set<Alpha*> s;
using RangeReturn = decltype(std::declval<std::set<Alpha*>&>() | ranges::v3::view::transform(std::function<Beta*(Alpha*)>()));
RangeReturn r();
};
Implementation, cpp file:
#include "Foo.h"
Foo::RangeReturn Foo::r() {
return s | ranges::v3::view::transform(std::function<Beta*(Alpha*)>{
[](Alpha* a) { return static_cast<Beta*>(a); }
});
}
This does the immediate job of hiding the actual implementation of Foo::r
.
However, the type of the return value still effectively "leaks" the information on how the range is constructed.
What is probably worse, I am now forced to explicitly use an std::function
object in the range pipeline.
But is that all information really needed by the user of the returned range? All the user of Foo::r
cares is that it is some kind of iterable. It has:
begin()
giving an iterator to the beginning of the rangeend()
giving some iterator or sentinelT
(Beta*
in the example case).The user does not care that there is a transform view or not, nor the number of transformations, filters and whatnot.
So, my question is -- is there a way to hide all that information? I would like to be able to write something like this:
In the header:
class Foo {
public:
std::set<Alpha*> s;
Iterable<Beta*> r();
};
In the cpp file:
#include "Foo.h"
Iterable<Beta*> Foo::r() {
return s | ranges::v3::view::transform([](Alpha* a) { return static_cast<Beta*>(a); });
}
I can accept the fact that the produced Iterable
type may contain real function calls that cannot be inlined due to the hiding process.
A link-time optimisation may or may not be able to optimise it later.
Unfortunately, to my knowledge, such Iterable
is merely a concept and not a separate type in the library.
The return type of r()
you are looking for is ranges::v3::any_view<Beta*>
.
Note that it applies type erasure that implies some runtime performance penalty which might be significant. Relevant discussion: poor performance of type-erased views.
Live demo: https://wandbox.org/permlink/JylKIHD0NaQsRXdB
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With