joboccara / NamedType

Implementation of strong types in C++
MIT License
769 stars 84 forks source link

Strongly typed indices in arrays #28

Closed Raikiri closed 4 years ago

Raikiri commented 5 years ago

Is there a solution to make an array with strong indexing? Such as:

using FooIndex = NamedType<size_t, struct FooTag>;
using BarIndex = NamedType<size_t, struct BarTag>;

std::vector<Foo, FooIndex> foos;
std::vector<Bar, BarIndex> bars;
...
BardIndex barIndex = ..;
FooIndex fooIndex = ...;
foos[barIndex] = Foo(); //should not compile
foos[fooIndex] = Foo(); //ok

Theoretically it's often fine to just deduce FooIndex = NamedType<size_t, Foo> for such containers. Is there a way to achieve it in an stl-compatible way?

joboccara commented 4 years ago

I think there are several ways:

Do you think either one of these solutions would allow to write the code you had in mind?

Raikiri commented 4 years ago

Yes, both of them achieve exactly what I had in mind, however, implementation-wise, inheriting from an stl container is generally not a great practice.

stellarpower commented 2 years ago

FWIW, some sort of half-automatic proxy wrapper might be great. I was hoping to combine this library with a units library, so that we have both semantic types, and correct phyiscal units/dimensions in our code. And all this would need to operate with matrix, vector, and quaternion types - quite a bit to juggle! So far, NamedType wrapping around boost::qvm is working quite nicely, albeit without the units support. This'll likely need some sort of proxy wrapper. We also have some Mats from OpenCV. This would be one example where being able to index into an opaque structure would be really powerful, if it could be done. QVM is so-designed that it's working nicely enough to have the NamedTypoe on the outside, as QVM is designed explicitly to operate on different (incompatible) types, and is thus mostly free-functions. But CV's Mat would not be so easy, and was one justifying reason for moving away from it and using this lib - access is always risky and very much relies on runtime support, including the type of data it's storing. It's all held behind one single type.

Tl;Dr - I imagine a composition proxy wrapper might work quite powerfully here - a more generic version of the reference version above, I guess - perhaps using some specialised traits to define how to access the wrapped object, with some sensible automatic defaults, like for operator[]. And if done well, I'd hope it would compile away to the same binary format, a bit like how std::chrono evaporates after build-time, and I believe NamedType itself does. Perhaps NamedContainer<Key, Value>, for a rough way of accessing - for things like list and vector, the key becomes more of an index. Am guessing map and set will work as-is, although may be wrong.