2

Assume I have a member variable std::vector<std::string> in a class and I want to return it from a member function as an immutable view using a combination of gsl::array_view and gsl::cstring_view. Unfortunately, the following doesn't compile:

class C {
public:
    gsl::array_view<const gsl::cstring_view<>> getVectorOfStrings() const 
    { 
         return _vectorOfStrings; 
    }

private:
    std::vector<std::string> _vectorOfStrings;
};

The reason for this is that there's no container of cstring_view that the array_view can be created from. So my question is: is there a way to use such a construct without explicitly adding something like a member of type std::vector<gsl::cstring_view<>>, which is clearly undesirable?

Edit

It seems to me that such 'transforming' views might be of more general use. Consider having a vector of owning pointers, such as std::vector<std::shared_ptr<T>>, which I'd like to expose to the user of the class as an array_view of raw pointers: gsl::array_view<const T*> without exposing my implementation-defined storage approach. Thoughts?

Rostislav
  • 3,857
  • 18
  • 30
  • With this theoretical `array_view` of `cstring_view`s, who would own the collection of `cstring_view`s, whose existence is required by the existence of the `array_view` of the `cstring_view`s? – jaggedSpire Oct 28 '15 at 14:25
  • @jaggedSpire Well, that's exactly what I stated in the last paragraph of my question. But perhaps there's a way of some kind of lazy-evaluating `array_view` which would convert whatever `string_view`-compatible array value to a `string_view` on request. I could probably write such a thing, but I thought that perhaps there's a better way. Thus this SO question :) – Rostislav Oct 28 '15 at 14:44
  • ah. If you want to refer to a non-owning collection of things that can be lightweight-convertible to `cstring_view`s, may I ask why the avoidance of an `array_view` of `std::strings`? – jaggedSpire Oct 28 '15 at 14:51
  • @jaggedSpire Yeah, that's actually what I have in the code now. But, what if, say, other subclass stores its strings as `std::vector`? It's kind of an 'academic' interest which might result in some contribution to gsl if other people deem it useful (who knows?). – Rostislav Oct 28 '15 at 15:11
  • The only alternative to those approaches I (with my limited experience :P) can think of would be to just construct a vector of `cstring_view`s and pass it out of the function to be converted as the client code sees fit. If the client needs it in exactly one expression, it can be returned as a temporary and the client can construct an `array_view` inline. Otherwise, they can just keep the vector and construct `array_view`s from it as needed. Of course you need to construct it every time you need it then, so it'd only make sense if you needed to pass an `array_view` into a function – jaggedSpire Oct 28 '15 at 15:20
  • Regarding your edit, how would you deal with the different sizes of `shared_ptr` and raw pointers while keeping the view lightweight? Keep track of the size of the underlying type, keep a character pointer, and multiply the underlying type's size by the desired offset and cast every time you want to index into the array view? I believe that `unique_ptr` has already covered much of the same issues, so maybe look there for potential issues with it – jaggedSpire Oct 28 '15 at 16:49
  • Ultimately, for type conversion flexibility and to allow the simpler semantics of `array_view` when needed you'd probably wind up writing a different but similar type with many of the same semantics, with an internal array_view, which automatically casts the type as you need it, as you were thinking about writing. Such a type would likely need an optional conversion functor supplied on construction to specify custom conversion behavior beyond what's allowed by explicit conversion. – jaggedSpire Oct 28 '15 at 17:19
  • @jaggedSpire Yep, sounds about right :) – Rostislav Oct 28 '15 at 17:26

1 Answers1

1

Per definition, views usually only provide references to existing objects. As a result, there is no way to create a normal array_view<const cstring_view<>> without first creating an matching container like e.g. a vector<const cstring_view<>>.

What you could do however, is to create your own specialization for gsl::array_view<const cstring_view<>>, that creates a cstring_view<> on demand (when the index operator is called and when the iterator is dereferenced). While this would save you a dynamic memory allocation and reduce the memory footprint compared to the naive approach, I don't it's worth the added complexity in most cases.

If you want to follow a generalized approach as described in your edit, you might want to have a look at boost::transform_iterator - either for direct use or as an inspiration for your own generalized transform_array_view class (which I'm sure, would be a welcome addition to the gsl or boost).

MikeMB
  • 20,029
  • 9
  • 57
  • 102
  • Well, after some more research, it seems to me that the `array_view` (or `span` as it is called now) is just not fit by design for this purpose. `span` expects contiguous storage of some data type, so basically, it could be processed by calling `data()`. In the end, I used `any_range` with `transformed` range adaptor. I still think it might be an interesting discussion, so I will raise a discussion on GSL's github. – Rostislav Nov 10 '15 at 15:17