Date: Wed, 2 Oct 2019 17:31:48 +1000
In my experience pointer-to-functions are rare in modern C++. Generally
people either use a deduced callable type template parameter (unconstained
currently but hopefully with the appropriate concept in C++20) for static
binding or they use std::function for dynamic binding. In both these cases
(unlike pointer-to-function) the parameter will bind to a lambda or other
callable, in particular something than can capture state or curry,
especially binding an implicit object parameter to a member function.
I've seen very experienced C++ programmers be surprised by the existence of
the function-to-pointer standard conversion - I see this as further
evidence that pointer-to-functions are little used. Some related searches
on ACTCD19 seem to confirm this too.
On Tue, Oct 1, 2019 at 9:25 PM John Adriaan via Std-Discussion <
std-discussion_at_[hidden]> wrote:
> Among other things, I teach programming; especially C++. Not in Academia
> (unfortunately), but Industry: mostly to experienced C programmers who need
> to upgrade.
>
>
>
> In my 30 years of the subject, I don’t remember when I personally learned
> about function pointers. But in C (and C++), I found that powerful
> feature’s syntax messy, to either code or explain, so I looked for a better
> way – and found it. It works in literally every compiler I’ve tried it on,
> both in C and C++. Moreover, when I extrapolated it to C++ for pointers to
> members and pointers to member functions, the syntax held.
>
>
>
> Yet I can honestly say that in all those years, no-one else I’ve talked
> to, let alone taught, has seen the syntax that I present below. Maybe I’m
> sheltered, or I’ve not met the right people – yet all the definitive works
> still show the more convoluted, albeit canonical syntax. So I now feel
> compelled to write: and my first foray is in the Wikipedia article
> dedicated to the subject. I reproduce it below, in full Wiki syntax, so
> that anyone can critique it; or even directly edit the article.
>
>
>
> My question is simple: why doesn’t everyone declare pointers to functions,
> members, or member functions in the way I describe below? Is this breaking
> some rule? Is the two-step process somehow inefficient? I’ve even found
> style guides, for significant companies, that said (paraphrased): “Do not
> put pointers (‘*’) inside typedefs, to more clearly show what the type is
> for. Note that typedefs for pointers to functions can ignore this rule.”
> No! Keeping this rule, especially for pointers to functions (and members,
> and pointers to members), makes them even easier to understand!
>
>
>
> To me, my most telling example is this two-step example, extracted from
> below:
>
>
>
> // This defines 'Fn', a type of function that accepts a 'char' and returns
> an 'int'.
>
> typedef int Fn(char c);
>
> …
>
> // This defines 'm', a pointer-to-member-of-'C' with type 'Fn',
>
> // and assigns the address of 'C::Member' to it.
>
> // You can read it right-to-left like all pointers:
>
> // "'m' is a pointer to member of class 'C' of type 'Fn'"
>
> Fn C::*m = &C::Member;
>
>
>
> Finally, I reiterate an earlier question posed on this forum by Shachar
> Shemesh (https://lists.isocpp.org/std-discussion/2019/06/0059.php):
>
> (Paraphrased) “Why is the precedence of ‘.*’ and ‘->*’ less than that of
> ‘.’ and ‘->’?”
>
>
>
> https://en.wikipedia.org/wiki/Function_pointer#Alternate_C_and_C++_Syntax
> (Note this is a sub-heading: #6 at the moment)
>
>
>
> == Alternate C and C++ Syntax ==
>
> The C and C++ syntax given above is the canonical one used in all the
> textbooks - but it's difficult to read and explain. Even the above
> <code>typedef</code> examples use this syntax. However, every C and C++
> compiler supports a more clear and concise mechanism to declare function
> pointers: use <code>typedef</code>, but ''don't'' store the pointer as part
> of the definition. Note that the only way this kind of <code>typedef</code>
> can actually be used is with a pointer - but that highlights the
> pointer-ness of it.
>
>
>
> === C and C++ ===
>
> <source lang="C">
>
> // This declares 'F', a function that accepts a 'char' and returns an
> 'int'. Definition is elsewhere.
>
> int F(char c);
>
>
>
> // This defines 'Fn', a type of function that accepts a 'char' and returns
> an 'int'.
>
> typedef int Fn(char c);
>
>
>
> // This defines 'fn', a variable of type pointer-to-'Fn', and assigns the
> address of 'F' to it.
>
> Fn *fn = &F; // Note '&' not required - but it highlights what is
> being done.
>
>
>
> // This calls 'F' using 'fn', assigning the result to the variable 'a'
>
> int a = fn('A');
>
>
>
> // This defines 'Call', a function that accepts a pointer-to-'Fn', calls
> it, and returns the result
>
> int Call(Fn *fn, char c) {
>
> return fn(c);
>
> } // Call(fn, c)
>
>
>
> // This calls function 'Call', passing in 'F' and assigning the result to
> 'call'
>
> int call = Call(&F, 'A'); // Again, '&' is not required
>
>
>
> // LEGACY: Note that to maintain existing code bases, the above definition
> style can still be used first;
>
> // then the original type can be defined in terms of it using the new
> style.
>
>
>
> // This defines 'PFn', a type of pointer-to-type-Fn.
>
> typedef Fn *PFn;
>
>
>
> // 'PFn' can be used wherever 'Fn *' can
>
> PFn pfn = F;
>
> int CallP(PFn fn, char c);
>
> </source>
>
>
>
> === C++ ===
>
> These examples use the above definitions. In particular, note that the
> above definition for <code>Fn</code> can be used in
> pointer-to-member-function definitions:
>
> <source lang="CPP">
>
> // This defines 'C', a class with similar static and member functions,
>
> // and then creates an instance called 'c'
>
> class C {
>
> public:
>
> static int Static(char c);
>
> int Member(char c);
>
> } c; // C
>
>
>
> // This defines 'p', a pointer to 'C' and assigns the address of 'c' to it
>
> C *p = &c;
>
>
>
> // This assigns a pointer-to-'Static' to 'fn'.
>
> // Since there is no 'this', 'Fn' is the correct type; and 'fn' can be
> used as above.
>
> fn = &C::Static;
>
>
>
> // This defines 'm', a pointer-to-member-of-'C' with type 'Fn',
>
> // and assigns the address of 'C::Member' to it.
>
> // You can read it right-to-left like all pointers:
>
> // "'m' is a pointer to member of class 'C' of type 'Fn'"
>
> Fn C::*m = &C::Member;
>
>
>
> // This uses 'm' to call 'Member' in 'c', assigning the result to 'cA'
>
> int cA = (c.*m)('A');
>
>
>
> // This uses 'm' to call 'Member' in 'p', assigning the result to 'pA'
>
> int pA = (p->*m)('A');
>
>
>
> // This defines 'Ref', a function that accepts a reference-to-'C',
>
> // a pointer-to-member-of-'C' of type 'Fn', and a 'char',
>
> // calls the function and returns the result
>
> int Ref(C &r, Fn C::*m, char c) {
>
> return (r.*m)(c);
>
> } // Ref(r, m, c)
>
>
>
> // This defines 'Ptr', a function that accepts a pointer-to-'C',
>
> // a pointer-to-member-of-'C' of type 'Fn', and a 'char',
>
> // calls the function and returns the result
>
> int Ptr(C *p, Fn C::*m, char c) {
>
> return (p->*m)(c);
>
> } // Ptr(p, m, c)
>
>
>
> // LEGACY: Note that to maintain existing code bases, the above definition
> style can still be used first;
>
> // then the original type can be defined in terms of it using the new
> style.
>
>
>
> // This defines 'FnC', a type of pointer-to-member-of-class-'C' of type
> 'Fn'
>
> typedef Fn C::*FnC;
>
>
>
> // 'FnC' can be used wherever 'Fn C::*' can
>
> FnC fnC = &C::Member;
>
> int RefP(C &p, FnC m, char c);
>
> </source>
>
>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>
people either use a deduced callable type template parameter (unconstained
currently but hopefully with the appropriate concept in C++20) for static
binding or they use std::function for dynamic binding. In both these cases
(unlike pointer-to-function) the parameter will bind to a lambda or other
callable, in particular something than can capture state or curry,
especially binding an implicit object parameter to a member function.
I've seen very experienced C++ programmers be surprised by the existence of
the function-to-pointer standard conversion - I see this as further
evidence that pointer-to-functions are little used. Some related searches
on ACTCD19 seem to confirm this too.
On Tue, Oct 1, 2019 at 9:25 PM John Adriaan via Std-Discussion <
std-discussion_at_[hidden]> wrote:
> Among other things, I teach programming; especially C++. Not in Academia
> (unfortunately), but Industry: mostly to experienced C programmers who need
> to upgrade.
>
>
>
> In my 30 years of the subject, I don’t remember when I personally learned
> about function pointers. But in C (and C++), I found that powerful
> feature’s syntax messy, to either code or explain, so I looked for a better
> way – and found it. It works in literally every compiler I’ve tried it on,
> both in C and C++. Moreover, when I extrapolated it to C++ for pointers to
> members and pointers to member functions, the syntax held.
>
>
>
> Yet I can honestly say that in all those years, no-one else I’ve talked
> to, let alone taught, has seen the syntax that I present below. Maybe I’m
> sheltered, or I’ve not met the right people – yet all the definitive works
> still show the more convoluted, albeit canonical syntax. So I now feel
> compelled to write: and my first foray is in the Wikipedia article
> dedicated to the subject. I reproduce it below, in full Wiki syntax, so
> that anyone can critique it; or even directly edit the article.
>
>
>
> My question is simple: why doesn’t everyone declare pointers to functions,
> members, or member functions in the way I describe below? Is this breaking
> some rule? Is the two-step process somehow inefficient? I’ve even found
> style guides, for significant companies, that said (paraphrased): “Do not
> put pointers (‘*’) inside typedefs, to more clearly show what the type is
> for. Note that typedefs for pointers to functions can ignore this rule.”
> No! Keeping this rule, especially for pointers to functions (and members,
> and pointers to members), makes them even easier to understand!
>
>
>
> To me, my most telling example is this two-step example, extracted from
> below:
>
>
>
> // This defines 'Fn', a type of function that accepts a 'char' and returns
> an 'int'.
>
> typedef int Fn(char c);
>
> …
>
> // This defines 'm', a pointer-to-member-of-'C' with type 'Fn',
>
> // and assigns the address of 'C::Member' to it.
>
> // You can read it right-to-left like all pointers:
>
> // "'m' is a pointer to member of class 'C' of type 'Fn'"
>
> Fn C::*m = &C::Member;
>
>
>
> Finally, I reiterate an earlier question posed on this forum by Shachar
> Shemesh (https://lists.isocpp.org/std-discussion/2019/06/0059.php):
>
> (Paraphrased) “Why is the precedence of ‘.*’ and ‘->*’ less than that of
> ‘.’ and ‘->’?”
>
>
>
> https://en.wikipedia.org/wiki/Function_pointer#Alternate_C_and_C++_Syntax
> (Note this is a sub-heading: #6 at the moment)
>
>
>
> == Alternate C and C++ Syntax ==
>
> The C and C++ syntax given above is the canonical one used in all the
> textbooks - but it's difficult to read and explain. Even the above
> <code>typedef</code> examples use this syntax. However, every C and C++
> compiler supports a more clear and concise mechanism to declare function
> pointers: use <code>typedef</code>, but ''don't'' store the pointer as part
> of the definition. Note that the only way this kind of <code>typedef</code>
> can actually be used is with a pointer - but that highlights the
> pointer-ness of it.
>
>
>
> === C and C++ ===
>
> <source lang="C">
>
> // This declares 'F', a function that accepts a 'char' and returns an
> 'int'. Definition is elsewhere.
>
> int F(char c);
>
>
>
> // This defines 'Fn', a type of function that accepts a 'char' and returns
> an 'int'.
>
> typedef int Fn(char c);
>
>
>
> // This defines 'fn', a variable of type pointer-to-'Fn', and assigns the
> address of 'F' to it.
>
> Fn *fn = &F; // Note '&' not required - but it highlights what is
> being done.
>
>
>
> // This calls 'F' using 'fn', assigning the result to the variable 'a'
>
> int a = fn('A');
>
>
>
> // This defines 'Call', a function that accepts a pointer-to-'Fn', calls
> it, and returns the result
>
> int Call(Fn *fn, char c) {
>
> return fn(c);
>
> } // Call(fn, c)
>
>
>
> // This calls function 'Call', passing in 'F' and assigning the result to
> 'call'
>
> int call = Call(&F, 'A'); // Again, '&' is not required
>
>
>
> // LEGACY: Note that to maintain existing code bases, the above definition
> style can still be used first;
>
> // then the original type can be defined in terms of it using the new
> style.
>
>
>
> // This defines 'PFn', a type of pointer-to-type-Fn.
>
> typedef Fn *PFn;
>
>
>
> // 'PFn' can be used wherever 'Fn *' can
>
> PFn pfn = F;
>
> int CallP(PFn fn, char c);
>
> </source>
>
>
>
> === C++ ===
>
> These examples use the above definitions. In particular, note that the
> above definition for <code>Fn</code> can be used in
> pointer-to-member-function definitions:
>
> <source lang="CPP">
>
> // This defines 'C', a class with similar static and member functions,
>
> // and then creates an instance called 'c'
>
> class C {
>
> public:
>
> static int Static(char c);
>
> int Member(char c);
>
> } c; // C
>
>
>
> // This defines 'p', a pointer to 'C' and assigns the address of 'c' to it
>
> C *p = &c;
>
>
>
> // This assigns a pointer-to-'Static' to 'fn'.
>
> // Since there is no 'this', 'Fn' is the correct type; and 'fn' can be
> used as above.
>
> fn = &C::Static;
>
>
>
> // This defines 'm', a pointer-to-member-of-'C' with type 'Fn',
>
> // and assigns the address of 'C::Member' to it.
>
> // You can read it right-to-left like all pointers:
>
> // "'m' is a pointer to member of class 'C' of type 'Fn'"
>
> Fn C::*m = &C::Member;
>
>
>
> // This uses 'm' to call 'Member' in 'c', assigning the result to 'cA'
>
> int cA = (c.*m)('A');
>
>
>
> // This uses 'm' to call 'Member' in 'p', assigning the result to 'pA'
>
> int pA = (p->*m)('A');
>
>
>
> // This defines 'Ref', a function that accepts a reference-to-'C',
>
> // a pointer-to-member-of-'C' of type 'Fn', and a 'char',
>
> // calls the function and returns the result
>
> int Ref(C &r, Fn C::*m, char c) {
>
> return (r.*m)(c);
>
> } // Ref(r, m, c)
>
>
>
> // This defines 'Ptr', a function that accepts a pointer-to-'C',
>
> // a pointer-to-member-of-'C' of type 'Fn', and a 'char',
>
> // calls the function and returns the result
>
> int Ptr(C *p, Fn C::*m, char c) {
>
> return (p->*m)(c);
>
> } // Ptr(p, m, c)
>
>
>
> // LEGACY: Note that to maintain existing code bases, the above definition
> style can still be used first;
>
> // then the original type can be defined in terms of it using the new
> style.
>
>
>
> // This defines 'FnC', a type of pointer-to-member-of-class-'C' of type
> 'Fn'
>
> typedef Fn C::*FnC;
>
>
>
> // 'FnC' can be used wherever 'Fn C::*' can
>
> FnC fnC = &C::Member;
>
> int RefP(C &p, FnC m, char c);
>
> </source>
>
>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>
Received on 2019-10-02 02:34:13