I'm trying to create and return a boost:any_range that contains only one object (I don't know if that's the core problem) but I get the following errors:
Below you can find the relevant code snippets:
const HandleRange BasicCollection::GetPartHandles() const
{
return HandleRange
//return -> also tried this
{
Handle(GenericHandleManager::CreatePartHandleValue(GenericHandleManager::GetPartIdx(_collectionHandle)))
};
}
Somehow this works, but that's not really clean:
const HandleRange BasicCollection::GetPartHandles() const
{
auto container = { _collectionHandle };
return container | boost::adaptors::transformed([collectionHandle = _collectionHandle](const auto & index_value)
{
return HandleRange::value_type
{
Handle(GenericHandleManager::CreatePartHandleValue(GenericHandleManager::GetPartIdx(collectionHandle)))
};
});
}
/**
* Defines an alias representing a boost range for handles.
*/
using HandleRange = boost::any_range<Handle, boost::forward_traversal_tag, const Handle>;
class Handle
{
public:
/**
* Construct a handle from a handle value.
* @param value The handle's value.
*/
inline explicit Handle(int_fast64_t value) noexcept : _value(value)
{
}
...
}
Thanks for any suggestions!
Somehow this works, but that's not really clean
That should not compile without diagnostics, as auto
is deduced as std::initializer_list<Handle>
.
That approach invokes Undefined Behaviour because the initializer list doesn't exist after returning.
The any_range
should be able to return an iterator range.
Pointers are iterators.
Any single object o
can be seen as a range [&o, &o + 1)
. That's a valid iterator range.
Combining these would already be a solution if GenericHandleManager::CreatePartHandleValue(...)
returns a reference:
const HandleRange BasicCollection::GetPartHandles() const {
Handle& h =
GenericHandleManager::CreatePartHandleValue(
GenericHandleManager::GetPartIdx(_collectionHandle));
return boost::make_iterator_range(&h, &h + 1));
}
If it returns a temporary, though, you'll need to make that "a range":
template <typename T>
struct SingletonRange : boost::iterator_range<T*> {
T val;
SingletonRange(T val)
: boost::iterator_range<T*>(std::addressof(val), std::addressof(val) + 1),
val(std::move(val))
{ }
};
Now you can safely¹ write (even though CreatePartHandleValue
returns a temporary):
HandleRange BasicCollection::GetPartHandles() const {
Handle h =
GenericHandleManager::CreatePartHandleValue(
GenericHandleManager::GetPartIdx(_collectionHandle));
return SingletonRange<Handle> {h};
}
#include <boost/range/iterator_range.hpp>
template <typename T>
struct SingletonRange : boost::iterator_range<T*> {
T val;
SingletonRange(T val)
: boost::iterator_range<T*>(std::addressof(val), std::addressof(val) + 1),
val(std::move(val))
{ }
};
struct Handle{};
struct GenericHandleManager {
static int GetPartIdx(Handle) { return 42; }
static Handle CreatePartHandleValue(int) { return {}; }
};
#include <boost/range/any_range.hpp>
using HandleRange = boost::any_range<Handle, boost::forward_traversal_tag, const Handle>;
struct BasicCollection {
HandleRange GetPartHandles() const;
private:
Handle _collectionHandle;
};
HandleRange BasicCollection::GetPartHandles() const {
Handle h =
GenericHandleManager::CreatePartHandleValue(
GenericHandleManager::GetPartIdx(_collectionHandle));
return SingletonRange<Handle> {h};
}
#include <iostream>
int main() {
BasicCollection coll;
for (Handle h : coll.GetPartHandles()) {
std::cout << "Handle in loop\n";
boost::ignore_unused_variable_warning(h);
}
}
Keep in mind that copying that range is as expensive as copying an
iterator_range<Handle*>
plus copying theHandle
itself. I'm assuming the Handle is lightweight (as usual for handles)
Prints
Handle in loop
¹ as long as you make sure you don't use any iterators from the SingletonRange after the lifetime of the range. This is a common C++ pattern though