Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #67534

Re: Tricky!

From Tim Rentsch <tr.17687@z991.linuxsc.com>
Newsgroups comp.lang.c++
Subject Re: Tricky!
Date 2019-08-10 14:19 -0700
Organization A noiseless patient Spider
Message-ID <86h86o90sb.fsf@mailhub.linuxsc.com> (permalink)
References <qhmmgr$dih$1@news.albasani.net> <7995a414-14ec-47ae-8afc-9ebcc520e30d@googlegroups.com> <qhn54k$dk6$1@dont-email.me> <86imrj8szn.fsf@mailhub.linuxsc.com> <qhppjb$ojs$1@dont-email.me>

Show all headers | View raw


"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> writes:

> On 30.07.2019 17:03, Tim Rentsch wrote:
>
>> "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> writes:
>>
>>> On 29.07.2019 15:30, Tiib wrote:
>>>
>>>> On Monday, 29 July 2019 14:49:58 UTC+3, Bonita Montero  wrote:
>>>>
>>>>> I need a class-function / method that returns a function-pointer
>>>>> to the same type of class-function.  How do I implement this?  I
>>>>> need this to have a kind of state-machine where each state-func-
>>>>> tion returns a pointer to the next state function.  I.e I would
>>>>> write:  "while( (fn = state->*fn()) != nullptr );".  I see no way
>>>>> to define the type of fn since it is recursive.
>>>>
>>>> Perhaps there are no way to make a function that return function
>>>> pointer of its own type.  However I do not understand the need
>>>> of it.
>>>
>>> I think she is inspired by (possibly someone else who is inspired
>>> by) an article by the Go language creator.
>>>
>>> As I recall his examples work nicely in Go, or assembly language, or
>>> a more dynamic language like Python, but have to be rewritten with
>>> some indirection for a language with strong static type checking
>>> like C++.
>>
>> The problem here is not strong static type checking but the lack
>> of recursive types in C++.  A language can have recursive types
>> and also have strong static type checking.
>>
>>> The reason for doing it that way, in languages where it's simple, is
>>> the textual simplicity of the source code.  Since it doesn't give
>>> that textual simplicity in C++, it's not a good way in C++.  It's
>>> doable, just not in any practical way.
>>
>> That depends on what is meant by "practical".
>
> Short and clear source code and reasonably good efficiency.
>
>
>>  It can be done like this:
>>
>>      #include <iostream>
>>
>>      class State {
>> 	using Nexter = ...;
>>
>>        public:
>> 	State(){}
>> 	Nexter r0(){  return  &State::r1;  }
>> 	Nexter r1(){  return  &State::r2;  }
>> 	Nexter r2(){  return  &State::rn;  }
>> 	Nexter rn(){  return  0;  }
>>      };
>>
>>      void
>>      run_state_machine(){
>> 	State *s = new State;
>>
>> 	for(  auto f = &State::r0;  f;  f = (s->*f)()  ){
>> 	    ::std::cout << "Got one\n";
>> 	}
>>      }
>
> Yes, that's mainly the basic technique I had in mind:  an extra level
> of indirection, the solution to every computer science problem.

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.

Back to comp.lang.c++ | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread


Thread

Tricky! Bonita Montero <Bonita.Montero@gmail.com> - 2019-07-29 13:49 +0200
  Re: Tricky! Öö Tiib <ootiib@hot.ee> - 2019-07-29 06:30 -0700
    Re: Tricky! "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-07-29 17:59 +0200
      Re: Tricky! Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-07-30 08:03 -0700
        Re: Tricky! "Rick C. Hodgin" <rick.c.hodgin@gmail.com> - 2019-07-30 11:20 -0400
        Re: Tricky! "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-07-30 18:00 +0200
          Re: Tricky! Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-08-10 14:19 -0700
    Re: Tricky! Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-07-30 07:31 -0700
      Re: Tricky! Öö Tiib <ootiib@hot.ee> - 2019-07-30 09:22 -0700
        Re: Tricky! Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-08-10 14:45 -0700
          Re: Tricky! Öö Tiib <ootiib@hot.ee> - 2019-08-11 03:23 -0700
            Re: Tricky! Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-08-17 22:02 -0700
              Re: Tricky! Öö Tiib <ootiib@hot.ee> - 2019-08-17 23:53 -0700
                Re: Tricky! Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-08-18 05:22 -0700
  Re: Tricky! Sam <sam@email-scan.com> - 2019-07-29 20:51 -0400
  Re: Tricky! Sam <sam@email-scan.com> - 2019-07-29 20:52 -0400
  Re: Tricky! Chris Vine <chris@cvine--nospam--.freeserve.co.uk> - 2019-07-30 13:58 +0100
    Re: Tricky! Bonita Montero <Bonita.Montero@gmail.com> - 2019-08-11 09:06 +0200

csiph-web