Where in the standard are functions returning functions disallowed?
Asked Answered
F

8

40

Where in the standard are functions returning functions disallowed? I understand they are conceptually ridiculous, but it seems to me that the grammar would allow them. According to this webpage, a "noptr-declarator [is] any valid declarator" which would include the declarator of a function:

int f()();

Regarding the syntax.

It seems to me that the syntax, as spelled out in [dcl.decl], allows

int f(char)(double)

which could be interpreted as the function f that takes a char and returns a function with same signature as int g(double).

1    declarator:
2       ptr-declarator
3       noptr-declarator parameters-and-qualifiers trailing-return-type
4    ptr-declarator:
5        noptr-declarator
6        ptr-operator ptr-declarator
7    noptr-declarator:
8        declarator-id attribute-specifier-seq opt
9        noptr-declarator parameters-and-qualifiers
10       noptr-declarator [ constant-expression opt ] attribute-specifier-seq opt
11       ( ptr-declarator )
12    parameters-and-qualifiers:
13       ( parameter-declaration-clause ) cv-qualifier-seqAfter

Roughly speaking, after 1->2, 2=4, 4->6, 4->6 you should have ptr-operator ptr-operator ptr-operator Then, use 4->5, 5=7, 7->8 for the first declarator; use 4->5, 5=7, 7->9 for the second and third declarators.

Film answered 13/7, 2015 at 15:18 Comment(14)
C way - return void * and cast it to function :) C++ way - return reference or pointer to object that have some function (strategy pattern)Revive
That's truly terrible advice. Even in C you can return a function pointer. In C++ you can use std::function or any callable object.Bui
Linux' signal function is a typical example of a function returning another function(-pointer), and I don't think that this is ridiculus. The only thing ridiculus is the syntax: void (*signal(int signo, void (*func )(int)))(int);Buhrstone
@maja: That does not return a function. It returns a function pointer.Betthel
@maja: Right, well, now you're making the same mistake as Anzurio. The OP did not say that returning function pointers is ridiculous. At all.Betthel
@LightnessRacesinOrbit Damn. Read the question as if OP wants to return function pointers and not actual functions.Buhrstone
You are wrong in thinking that C++ is mostly defined by a grammar. The grammar does not means a lot (and is known to be extremely ambiguous for C++; this is why coding C++ parsers is boringly difficult, and so is reading C++ code!)Caveator
I finally downvoted the question, because it is mixing grammar and language definition which for C++ particularly (and sadly IMHO) is very different. This is why C++ is so difficult to read, difficult to learn, difficult to parse (all this because of legacy and backward look&feel compatibility with C reasons)Caveator
@BasileStarynkevitch: grammar and language definition were shaken, not stirred. So, why is the mix bad?Film
You mention that the grammar would allow them, and you are right; it is not the grammar which forbids that. But I've understood your question as asking for some syntax rule forbidding it (and there are none, since it is a semantic issue)Caveator
@BasileStarynkevitch The question is "Where in the standard are functions returning functions disallowed?" It's not qualified by asking for a specific syntax rule.Applaud
@Barry: the next sentence in the question is ending "it seems to me that the grammar would allow them", so can be understood (at least by the non-native English speaker that I am) as why the grammar is forbidding that...Caveator
There is nothing conceptually ridiculous about functions returning functions. It's done all the time in other languages. it is even done in C++ in the form of functions returning closures.Spinnaker
Possible duplicate of Function returning a lambda expressionRuffled
A
59

From [dcl.fct], pretty explicitly:

Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things. There shall be no arrays of functions, although there can be arrays of pointers to functions.

With C++11, you probably just want:

std::function<int()> f();
std::function<int(double)> f(char);

There is some confusion regarding the C++ grammar. The statement int f(char)(double); can be parsed according to the grammar. Here is a parse tree:

grammar

Furthermore such a parse is even meaningful based on [dcl.fct]/1:

In a declaration T D where D has the form

    D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
        ref-qualifieropt exception-specificationopt attribute-specifier-seqopt

and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list function of (parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt returning T”.

In this example T == int, D == f(char)(double), D1 == f(char). The type of the declarator-id in T D1 (int f(char)) is "function of (char) returning int". So derived-declarator-type-list is "function of (char) returning". Thus, the type of f would be read as "function of (char) returning function of (double) returning int."

It's ultimately much ado about nothing, as this is an explicitly disallowed declarator form. But not by the grammar.

Applaud answered 13/7, 2015 at 15:32 Comment(6)
Notice that it is not a grammar thing. The C++ grammar (in quasi EBNF notation) probably allows returning functions. The semantics forbid it.Caveator
The standard is over 1000 pages. It is easy to overlook stuff. In this case, that is paragraph 10 ... too way down ...Film
@Film That's why we have PDFs with bookmarks. Jump to the Functions section (8.3.5), then search for "return"Applaud
@Barry: no need to be condescending. I did search in 8.4 then the first two pages of 8.3.5. By that point, I thought I did not understand something.Film
@Film I wasn't intending to be condescending. I thought you were reading the paper version and was suggesting a more convenient alternative.Applaud
The parsing graph is very nice and I am curious how you generate it. What tool is used to generate such graph, it would be very useful for learning! Can you share some information here?Quirites
C
7

With C++11 (but not previous versions of C++) you can not only return C-like function pointers, but also C++ closures, notably with anonymous functions. See also std::function

The standard disallows (semantically, not syntactically - so it is not a question of grammar ; see Barry's answer for the citation) returning functions (and also disallow sizeof on functions!) but permits to return function pointers.

BTW, I don't think that you could return entire functions. What would that mean? How would you implement that? Practically speaking, a function is some code block, and its name is (like for arrays) a pointer to the start of the function's machine code.

A nice trick might be to build (using mechanisms outside of the C++ standard) a function at runtime (and then handling its function pointer). Some external libraries might permit that: you could use a JIT library (e.g. asmjit, gccjit, LLVM ...) or simply generate C++ code, then compile and dlopen & dlsym it on POSIX systems, etc.

PS. You are probably right in understanding that the C++11 grammar (the EBNF rules in the standard) does not disallow returning functions. It is a semantic rule stated in plain English which disallows that (it is not any grammar rule). I mean that the EBNF alone would allow:

 // semantically wrong... but perhaps not syntactically
 typedef int sigfun_T(std::string);
 sigfun_T foobar(int);

and it is for semantics reasons (not because of EBNF rules) that a compiler is rightly rejecting the above code. Practically speaking, the symbol table matters a lot to the C++ compiler (and it is not syntax or context-free grammar).

The sad fact about C++ is that (for legacy reasons) its grammar (alone) is very ambiguous. Hence C++11 is difficult to read (for humans), difficult to write (for developers), difficult to parse (for compilers), ....

Caveator answered 13/7, 2015 at 15:29 Comment(17)
"The standard disallow[s] returning functions" does not answer the question "which part of the standard disallow[s] returning functions".Betthel
"BTW, I don't think that you could return entire functions. What would that mean? How would you implement that." Right, the OP said that in the question. Nobody can read today :(Betthel
Actually, with a jit library, you could return a function; or at least a block of memory containing one.Naara
@CofeeandCode: technically, a JIT library would return a function pointer and build a function code in the address space.Caveator
@BasileStarynkevitch you could write a struct func{unsigned char[1024];}; and return that, then you would be returning the whole function.Naara
But that usually won't work. And pedantically, you are then returning a struct not a function.Caveator
@LightnessRacesinOrbit: Just being the devil's advocate here. If we return a processor instruction (which might fit in a register), we might be returning a function.Film
Most processors are unable to execute an instruction in a register. IIRC, IBM/360 had an "Execute" machine instruction, but even that needed a memory address....Caveator
@Hector: Not really. The type system exists!Betthel
@LightnessRacesinOrbit: I'll advocate for the devil again. The interpretation of what is being held in a register is not part of C++. It could be a function. I was addressing the comments about JIT libraries returning pointers to functions instead of functions.Film
@Hector: The question is about C++. What you do with some registers in some physical implementation has nothing to do with it. :)Betthel
@LightnessRacesinOrbit Imagine returning a variable-size byte array that happens to contain machine code. (The variable-size array itself; not a pointer to it). Note that in C and C++ all variables have fixed sizes, so you can't return a variable-size object, so you can't return a function. (But you can return a pointer to one)Resistless
@immibis: And even if you could, it would still not be a function. It would be an array.Betthel
@LightnessRacesinOrbit If the language allowed you to return functions, that is what returning a function would do - it would return a function. Functions happen to be represented by variable-length byte arrays on all machines I'm aware of, but for the language's purposes, it would be a function not an array.Resistless
@immibis: Right, well that's not what you said a moment ago. :) Again, the type system exists and I deliberately do not ignore it. Not really fussed about implementation details as that is not what we are talking about here.Betthel
@LightnessRacesinOrbit You asked what it would mean to return a function, I answered. Think of the difference between returning a pointer to an array, and returning the array itself - that's the difference between returning a pointer to a function, and returning the function itself. Hypothetically.Resistless
@immibis: I didn't ask anything!Betthel
F
5
using namespace std;

auto hello()
{
    char name[] = "asdf";
    return [&]() -> void
    {
        printf("%s\n", name);
    };
}

int main()
{
    hello()();
    auto r = hello();
    r();
    return 0;
}
Flavone answered 27/8, 2020 at 16:23 Comment(3)
While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.Meow
Sorry, I am a c++ rookie, I only know that writing code like this can solve the problem, but I don’t understand how this code worksFlavone
The auto is probably deduced to be a function pointer, not a functionAdolf
E
0

The formal grammar of C actually disallows returning functions but, one can always return a function pointer which, for all intents and purposes, seems like what you want to do:

  int (*getFunc())(int, int) { … }

I am fixated in saying, the grammar, is a more fundamental explanation for the lack of support of such feature. The standard's rules are a latter concern.

If the grammar does not provide for a way to accomplish something, I don't think it matters what the semantics or the standard says for any given context-free language.

Earleneearley answered 13/7, 2015 at 15:23 Comment(14)
How do you find useful something that is impossible?Betthel
@LightnessRacesinOrbit I meant something like "returning functions by means of function pointers".Earleneearley
Well, that's not what the OP said.Betthel
"The standard disallow[s] returning functions" does not answer the question "which part of the standard disallow[s] returning functions".Betthel
@LightnessRacesinOrbit sigh Since it is so important to you (don't want to speculate why), I am removing that part of my answer. I reckon my actual answer may not really addresses the question; I am just trying to be helpful and not a self-righteous individual.Earleneearley
@Earleneearley I don't think you understand LRiO's comment. What OP meant by "ridiculous" was returning functions as a value. You saying you find it useful doesn't make sense, since that isn't even legal C or C++ code. You're saying returning functions by pointer/reference/functor is useful - that is undoubtly true, but also sort of irrelevant to the question.Applaud
@Anzurio: Yes it is terrible that the quality of the content on this website is important to meBetthel
That part with my personal opinion was removed. I have no idea what else do you want? From my POV, the question is flawed because it's not the standard's "fault". The grammar does not permit returning functions.Earleneearley
It is not the grammar which disallow returning functions. It is the semantics (and unfortunately, most of C++11 semantics is described informally in technical English)Caveator
@BasileStarynkevitch do you mind pointing me to the construct and/or path that allows it? I'd really like to know.Earleneearley
@BasileStarynkevitch I sustain is a grammar issue because it will be the pushdown automaton which will reject a program that "tries" to return a function.Earleneearley
I edited my answer to improve it. The codechunk starting with typedef int sigfun_T(std::string); is legal C++ syntax but it has no meaningCaveator
I downvoted, because returning a function is forbidden by something which is not the grammar (but the "semantics").Caveator
I posted a parse tree to illustrate that the grammar does allow this.Applaud
R
0

Actually in C one cannot pass or return function. Only a pointer/address of the function can be passed/returned, which conceptually is pretty close. To be honest, thanks to possiblity of ommiting & and * with function pointers one shouldn't really care if function or pointer is passed (unless it contains any static data). Here is simple function pointer declaration:

void (*foo)();

foo is pointer to function returning void and taking no arguments.

In C++ it is not that much different. One can still use C-style function pointers or new useful std::function object for all callable creations. C++ also adds lambda expressions which are inline functions which work somehow similar to closures in functional languages. It allows you not to make all passed functions global.

And finally - returning function pointer or std::function may seem ridiculous, but it really is not. For example, the state machine pattern is (in most cases) based on returning pointer to function handling next state or callable next state object.

Recombination answered 13/7, 2015 at 15:47 Comment(0)
F
0

Unless you are returning a pointer or reference to a function which is ok, the alternative is returning a copy of a function. Now, think about what a copy of a function looks like, acts like, behaves like. That, first of all would be an array of bytes, which isn't allowed either, and second of all those bytes would be the equivalence of a piece of code literally returning a piece of code....nearly all heuristic virus scanners would consider that a virus because there would also be no way to verify the viability of the code being returned by the runtime system or even at compile time. Even if you could return an array, how would you return a function? The primary issue with returning an array (which would be a copy on the stack) is that the size is not known and so there's no way to remove it from the stack, and the same dilemma exists for functions (where the array would be the machine language binary code). Also, if you did return a function in that fashion, how would you turn around and call that function?

To sum up, the notion of returning a function rather than a point to a function fails because the notion of that is a unknown size array of machine code placed (copied) onto the stack. It's not something the C or C++ was designed to allow, and with code there is now way to turn around and call that function, especially i you wanted to pass arguments.

I hope this makes sense

Footman answered 18/7, 2015 at 6:35 Comment(0)
G
0

in kinda sense,function_pointer is funciton its self,
and "trailing return type" is a good and hiding thing in c++11,i will rather write like this:

#include<iostream>



auto return0(void)->int
{return 0;}
auto returnf(void)->decltype(&return0)
{return &return0;}



/*Edit: or...
auto returnf(void)->auto (*)(void)->int
{return &return0;}
//that star means function pointer
*/




auto main(void)->int
{
    std::cout<<returnf()();
    return 0;
}

(if type unmatch,try apply address operator "&")
look,so much of sense.

but down side of this is that "auto" thing in head of function,
that`s non-removable and no type could match it (even lambda could match template type std::function<>) but if you wish,macro could do magic(sometime curse) to you

#define func auto
Garter answered 14/4, 2020 at 4:34 Comment(0)
C
0

You can still do that but in order to do it you need to set the return type like this std::function<return type(parameters)>. To call the function simply just do what you would normally do. Example:

std::function<void()> return_function {
    std::function<void()> myFunction();
    return myFunction
}
Coppage answered 30/8, 2023 at 16:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.