Reputation: 21974
Say I have a struct with a bunch of members:
struct foo {
int len;
bar *stuff;
};
As it so happens stuff
will point to an array of bar
s that is len
long. I'd like to encode this in stuff
's type. So something like:
struct foo {
int len;
DependentLength<bar, &foo::len> stuff;
};
Then I could implement DependentLength
to behave like a pointer to a bar array but that asserts when trying to looking at an index bigger than foo::len
. However, I can't implement DependentLength<&foo::len>::operator[]
because operator[] only takes one parameter, the index, and it needs to know the location of the 'foo' object in order to dereference the member pointer template parameter and do the assert check.
However, I happen to know that DependentLength will only ever be used here as a member of 'foo'. What I'd really like to do is tell DependentLength where to find len relative to itself, rather than relative to a foo pointer. So something like DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;
, but that's not legal C++. Is there a good or failing that evil language hack that could make this work?
Upvotes: 2
Views: 120
Reputation: 3934
You can tell the template the offset of the member to use as length.
template<typename T, typename LEN_T, ptrdiff_t LEN_OFFSET>
class DependentArray
{
public:
T& operator[](LEN_T i_offset)
{
if (i_offset < 0) throw xxx;
if (i_offset > this->size()) throw xxx;
return this->m_pArr[i_offset];
} // []
private:
LEN_T& size()
{
return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + LEN_OFFSET);
} // ()
private:
T* m_pArr;
};
struct foo
{
int len;
DependentArray<bar, int, -sizeof(int)> stuff;
};
Edit 2:
Thought of another solution. Use a class that is good for foo only to supply the offset of the size field and define its method after foo is defined and offsets can be calculated:
#define MEMBER_OFFSET(T,M) \
(reinterpret_cast<char*>(&reinterpret_cast<T*>(0x10)->M) - \
reinterpret_cast<char*>(reinterpret_cast<T*>(0x10)))
template<typename T, typename LEN_T, typename SIZE_OFFSET_SUPPLIER>
class FooDependentArray
{
public:
T& operator[](LEN_T i_offset)
{
if (i_offset < 0) throw xxx;
if (i_offset > this->size()) throw xxx;
return this->m_pArr[i_offset];
} // []
private:
LEN_T& size()
{
const ptrdiff_t len_offest = SIZE_OFFSET_SUPPLIER::getOffset();
return *reinterpret_cast<LEN_T*>(reinterpret_cast<char*>(this) + len_offset);
} // ()
private:
T* m_pArr;
};
struct FooSizeOffsetSupplier
{
static ptrdiff_t getOffset();
};
struct foo
{
int len;
DependentArray<bar, int, FooSizeOffsetSupplier> stuff;
};
ptrdiff_t FooSizeOffsetSupplier::getOffset()
{
return MEMBER_OFFSET(Foo,m_len) - MEMBER_OFFSET(Foo,m_pArr);
} // ()
This makes it possible to add and remove members from foo.
Upvotes: 2
Reputation: 32540
So something like
DependentLength<(char*)&foo::stuff - (char*)&foo::len> stuff;
You're asking templates to perform calculations based on dynamic properties passed to them during run-time ... that won't work for templates since they must be instantiated with values that allow the compile to create the code requested by the template parameters at compile time
. Thus any values passed to a template must be resolvable at compile-time, and not run-time.
You're going to have to use a dynamic container type. For instance, std::vector
meets your request where the std::vector::at()
function will throw an exception if you exceed the bounds of the underlying container. It's unfortunately not as convenient as a static_assert
, but again, using static_assert
is impossible for this situation since you need run-time checking for the bounds. Additionally, std::vector
also incorporates an overload for operator[]
, iterators, queries for it's size, etc.
Upvotes: 3