Can I fulfill the call counter statement at compile time?

I have a class that is simple on the interface like this:

struct Foo { inline Foo & operator << (int i) { return *this; } }; 

Then I can use it as follows:

 Foo foo; foo << 1 << 2 << 3 << 4; 

Now I would like to limit the use of this operator. For example , I would like it to be called an even number of times between points in a sequence.

I am currently addressing this issue using the inner proxy class. A temporary structure is created, which is destroyed at the end of the control sequence and checks how many times the operator has been called:

 struct Foo { inline Foo() : m_count(0) {} private: struct FooProxy { friend struct Foo; inline ~FooProxy(); inline struct Foo & operator << (int i); private: inline FooProxy(struct Foo &foo) : m_foo(foo) {} struct Foo &m_foo; }; public: inline FooProxy operator << (int i); private: int m_count; }; inline Foo::FooProxy Foo::operator << (int i) { ++m_count; return FooProxy(*this); } inline Foo & Foo::FooProxy::operator << (int i) { ++m_foo.m_count; return m_foo; } inline Foo::FooProxy::~FooProxy() { assert(m_foo.m_count % 2 == 0); } 

There are a few caveats, but basically this does the job:

 Foo foo; foo << 1 << 2 << 3 << 4; /* is OK */ foo << 1 << 2 << 3; /* triggers an assert */ 

Now I'm wondering if there is a way to ensure this at compile time, either using the same proxy technology or using a different strategy.

Another example of what I would like to achieve: forcing at least one int after any float been passed to the operator:

 foo << 1 << 2 << 3.f << 4.f << 5; /* is OK */ foo << 1 << 2 << 3.f << 4.f; /* illegal because one `int` is needed */ 
+4
source share
2 answers

You can use a template proxy that will encode state as a template parameter, not a member.

However, if you are not using any return value for something, you can only check some conditions, but not others. For example, you can check that an int was inserted before the float or that two floats were not inserted in the row, but you cannot check if an int is inserted after any floats.

As a rule, you can detect any condition that must be met before the next insertion, simply by highlighting the input operator for something invalid for invalid states. But you cannot check the final state, because all proxies must be destructible (each is different, so all intermediate ones will be destroyed).

+1
source

Why not use something like FooPair for parity:

 struct FooPair { int m_x, m_y; FooPair(int x, int) : m_x(x), m_y(y) { } }; 

and

 inline Foo & operator << (const FooPair &pair) { return *this; } 

so that people call it the following:

 Foo foo; foo << FooPair(1,2) << FooPair(3,4); 

This is more verbose, but will provide an even number of values.

+2
source

Source: https://habr.com/ru/post/1492925/


All Articles