Not a level of indirection, a layer of abstraction. Those aren't
the same thing. The abstraction here is a five-minute exercise
to write, ignorable in most of the defining class (that's one
value of using an abstraction), completely invisible to client
code, and incurs no run-time cost. So I think it meets the
criteria you gave.
> Nit-picks: (1) a State instance doesn't actually hold state, yet the
> member functions are not `static`, and (2) there's a needless dynamic
> allocation and memory leak.
In cases like this where I am sketching out a solution to someone
else's question, I assume without really thinking about it that
people who read my comments are tracking enough to focus on those
aspects that are relevant to the original problem, and not be
distracted by incidental matters. Apparently that was a bad
assumption here. Taking up your points in reverse order:
(1) Yes the code above does a 'new' but not a 'delete'. That has
no important bearing on the mechanism I was trying to explain.
(2) We don't know that the dynamic allocation is needless,
because we don't know what or how much member data the 'State'
class might have in the context of the OP's question.
(3) The member functions are regular member functions, because
that's what the OP was asking about, and because, based on what
question was asked, it seems obvious that the object in question
is expected to have internal state (aka member data) that is
manipulated by the various 'Nexter' member functions called
within the object.
> Fixing those issue, it can go like this (in practice one would have
> some input argument to each function, so it can decide which state to
> go to):
>
>
> class State
> {
> struct Nexter
> {
> using Func = auto() -> Nexter;
> Func* m_p_func;
>
> operator Func* () const { return m_p_func; }
> Nexter( Func* const f ): m_p_func( f ) {}
> };
>
> State() = delete;
>
> public:
> static auto r0() -> Nexter { return &State::r1; }
> static auto r1() -> Nexter { return &State::r2; }
> static auto r2() -> Nexter { return &State::rn; }
> static auto rn() -> Nexter { return nullptr; }
> };
>
> #include <iostream>
> using namespace std;
>
> auto main() -> int
> {
> for( auto f = &State::r0; f; f = f() ) {
> cout << "State "
> << reinterpret_cast<void const*>( f )
> << "." << endl;
> }
> }
>
> Here the reinterpret_cast is [...]
I skipped over any questions about whether the cast would have
problems since they aren't important to what you're explaining.
The OP included a code fragment that clearly showed calling
member functions through pointer-to-member-function return
values. (This code fragment appears in a quoted portion above.)
Is there some reason you're giving an example that doesn't
conform to that interface?
>> with a fairly short (five-ish lines) elaboration for the "...".
>
> Not sure what you're thinking of there?
Much like yours but using pointer-to-member-function rather than
pointer-to-function, to wit:
using Nexter = struct Hidden {
using PMF = Hidden (State::*)();
Hidden( PMF p0 ) : p( p0 ) {}
operator PMF(){ return p; }
PMF p;
};
>> Personally I think this approach offers a fairly nice textual
>> simplicity, and is eminently practical.
>>
>> If someone thinks five lines of overhead is too much, the "..."
>> can be made into a template, giving
>>
>> using Nexter = WrapMemberFunctionPointer< State >;
>>
>> which provides an even nicer source text.
>
> Well, to my mind the indirection adds complexity so that even though
> the code is short it's not IMO /clear/,
I don't think of what I suggested as an indirection, but as an
abstraction. A principal value of abstraction is to hide complexity
by tucking it away somewhere so it can be ignored afterwards. Here
I think that virtue is achieved rather nicely.
If someone were to think of a scheme along these lines not as an
abstraction but as an indirection, and one which is not meant to
hide inner workings, that could very well be confusing. I don't
know why someone would want to think of this approach that way.
> and for debugging it's not so
> clear which state one is in, just by looking at a function address.
I think how you are conceptualizing the problem is not the same
as how the OP is conceptualizing the problem. From how the
question was asked I inferred that 'State' is not just the
integer state of a finite state machine, but something more
elaborate (and perhaps much more elaborate) that knows what to do
to accomplish state transitions as well as which state should be
"gone to". And there may not be a one-to-one mapping between
'Nexter' member functions and states of the FSM that the 'State'
object is in; that information may not even be available from
just knowing what the pointer value is. If we want to know
something about what state the object is in, presumably we should
ask the object using some other (not yet stated) member function.
> But, subjective opinion. ;-)
I suspect your opinion here is shaped largely by how you are framing
the question. I took the OP at face value that what was being asked
for is in fact what is wanted. It's interesting that in this thread
three different people have suggested solutions to problems other
than what OP was asking about.