0

I'm writing an iterator facade, but it seems like my iterator will violate even basic Iterator concept. The problem is that the iterator accepts any Callable, which might render fundamental operations on Regular Types ill formed. I'm following definition presented by Alexander Stepanov. Here is the declaration:

template<typename Callable, typename Iterator>
class transform_iterator 

Question: How to wrap callable to make it regular type?

Actually I need wrapper only to be:

  • Copy constructible

  • Copy assignable

  • Destructible

and not Regular Type in general.

My first attempt: wrap into std::optional<>:

template <typename T>
class regular_box
{
    std::experimental::optional<T> value;
public:

//constructors, operator=

    template <typename ... ArgTypes>
    auto operator()(ArgTypes&& ... args)
    {
        return callable.value()(std::forward<ArgTypes>(args)...);
    }
};

But it doesn't solve the problem with assignment, since if Callable is not copy assignable, the regular_box will not be copy assignable as well.

Plan B: std::function<>. Though I would like to postpone it for as long as possible.

I could make multiple levels of fallbacks, but I couldn't find any better solution than that.

Incomputable
  • 2,188
  • 1
  • 20
  • 40
  • 2
    I don't understand what you are trying to achieve and what the problem is. – bolov Mar 08 '17 at 11:41
  • @bolov, I store Callable as member of my transform iterator. So, if it is not regular, it will violate Iterator concept. – Incomputable Mar 08 '17 at 11:42
  • @Incomputable I'm not sure what you mean by "regular". Do you mean "copyable"? – TartanLlama Mar 08 '17 at 11:44
  • 1
    @TartanLlama, added the link to the paper by Alexander Stepanov. There is a table, which is actually the vital part. – Incomputable Mar 08 '17 at 11:44
  • @TartanLlama http://stackoverflow.com/questions/13998945/what-is-a-regular-type-in-the-context-of-move-semantics – kennytm Mar 08 '17 at 11:45
  • You can limit Callable to a copyable type, or it doesn't make much sense. – Tatsuyuki Ishi Mar 08 '17 at 11:46
  • @TatsuyukiIshi, I would like to try to push the limit. May be there is hack to do it. I don't really care how dirty it is. – Incomputable Mar 08 '17 at 11:47
  • @Incomputable Why do you need "DefaultConstructible"? An Iterator does not need to be DefaultConstructible, and your Iterator member is almost always never DefaultConstructible. – kennytm Mar 08 '17 at 11:48
  • @kennytm, you're right, thanks. Removed it. – Incomputable Mar 08 '17 at 11:49
  • if the callable it's not copyable then you have to store a reference (reference type or pointer type) to it, either directly, or via a proxy_container. And you have to watch for dangling references. I don't see a way around it. – bolov Mar 08 '17 at 11:51
  • Ah, I understand now. What would you want the copy constructor to do if `Callable` is not copyable? Store references which could dangle, or issue a compiler error? – TartanLlama Mar 08 '17 at 11:51
  • @TartanLlama, I believe I will just keep falling back until `std::function` then. – Incomputable Mar 08 '17 at 11:52
  • @Incomputable But `std::function` can't store non-copyable types either. – TartanLlama Mar 08 '17 at 11:55
  • I'm pretty sure that you need `std::function`, or the moral equivalent of std::function in what you implement yourself. – Caleth Mar 08 '17 at 11:55
  • @TartanLlama, you're right. I can implement it by hand then ... It's gonna be (not) fun. – Incomputable Mar 08 '17 at 11:56
  • @Incomputable How do you plan to use your iterator? You could use something like [function views](https://vittorioromeo.info/index/blog/passing_functions_to_functions.html), but only if your iterators will never outlive the `Callable` which they reference. – TartanLlama Mar 08 '17 at 12:01
  • @TartanLlama, it actually wraps the underlying iterator. So usage vectors are probably unlimited, even in multithreaded context. `std::shared_ptr<>` would be great in that case though. – Incomputable Mar 08 '17 at 12:02

1 Answers1

1

There's some way to get such a type:

  1. Take a reference and store in reference_wrapper.
    Reference is obviously copy constructible.

  2. If you want to own the callback and manage the lifetime, store it in shared_ptr.
    This adds many overhead, but it just works.

Tatsuyuki Ishi
  • 3,883
  • 3
  • 29
  • 41
  • I think that `std::function` would be much better fit then. – Incomputable Mar 08 '17 at 11:51
  • std::function erases the type and incur more overhead. If you're doing templates, do not use it. – Tatsuyuki Ishi Mar 08 '17 at 11:53
  • I think it should be measured to say for sure. I've heard that type erasure will lose effects when used a lot on the same type. – Incomputable Mar 08 '17 at 11:54
  • 1
    a std::shared_ptr based implementation would also need to erase the type and incur similar overhead – Caleth Mar 08 '17 at 11:54
  • It's unclear that what you're trying to add. std::function still needs the Callable being CopyConstructible, and a reference wrapper is much minimal for adding CopyAssignable. – Tatsuyuki Ishi Mar 08 '17 at 11:56
  • 1
    @Caleth shared_ptr doesn't necessarily erase the type. Since the concrete type is provided in template argument, the overhead here is reference counting. – Tatsuyuki Ishi Mar 08 '17 at 11:59
  • @TatsuyukiIshi If the callable is destroyed, your reference_wrapper becomes dangling. shared_ptr would be the best. – kennytm Mar 08 '17 at 12:02