C++'s annoying limits... (is there something better?)

This topic is 4798 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

Does anyone know of extensions to C++ or a C-like language which will allow me to use nested functions, compose them, combine them with anonymous functions and and pass them to other functions? These features would save me a ton of time in writing my current game engine. I've looked at boost::lambda and fc++, but they are unbearably ugly hacks. (example of nested functions)
void main()
{
int func1 (int x, int y)
{
return x+y;
}

cout << endl << func1(3, 2)
}


(example of composed functions)

/* add this code to the main function above */
int func2 = func1(35);

cout << endl << func2(62); //calls func1(35, 62)


(example of passing a function, combined with an anonymous function)
/* add this code to the main function above */

int List[3] = { 1, 2, 3 }

foreach(List, List+3, cout << func2(arg1) );
/* call func2(List) for i 0-2 */


Any ideas? Thanks, Alex

Share on other sites
D, Java, Lisp and Pascal support some or all of those features.
And don't forget that C++ supports nested functions through local class members, some compilers even have them as an extension (GCC).

Share on other sites
Lots of languages support closures and first-class functions. Some I'm more familiar with are LISP, Ruby, Python, LUA, or any 'functional' languages.

Python! [smile]

Share on other sites
Ananymous functions? i think a FUNCTION POINTER would take care of that easily, look it up, as for this "nested function" stuff... you want to add in functions into the middle of other functions at run time? that hardly seems useful or necessary, what are you trying to accomplish? i sincerely doubt there isnt an elegant solution

hope that helps
-Dan

Share on other sites
Quote:
 Original post by Ademan555i think a FUNCTION POINTER would take care of that easily

A function pointer does not carry context.

Quote:
 that hardly seems useful or necessary, what are you trying to accomplish?

Closures are extremely powerful constructs.

Quote:
 i sincerely doubt there isnt an elegant solution

In standard C++, there isn't really. He has already pointed out at the mainstream solutions (boost and FC++).

Share on other sites
Since you appear to be aware that anonymous functions and function composition/combinations are missing from C++, what languages let you use them? :) Can't you use that?

Share on other sites
I don't recommend the use of this, but it will do one of the things you want:
#define LOCAL_FUNCTION_START struct temp_local_struct { static #define LOCAL_FUNCTION_END };#define LOCAL_FUNCTION_CALL temp_local_struct::int main() {   LOCAL_FUNCTION_START   int func1 (int x, int y)   {     return x+y;    }   LOCAL_FUNCTION_END   cout << LOCAL_FUNCTION_CALL func1(3, 2) << endl;   return 0;}

Share on other sites
Quote:
 Original post by cypherxDoes anyone know of extensions to C++ or a C-like language which will allow me to use nested functions, compose them, combine them with anonymous functions and and pass them to other functions? These features would save me a ton of time in writing my current game engine. I've looked at boost::lambda and fc++, but they are unbearably ugly hacks. ...Any ideas?
I know it's not at all C like, but Haskell has probably the best support of these features of any language. Your examples:
-- Example of Nested Functionsmain = do         putStr $"\n" ++ func1 3 2 where func1 x y = x + y-- Example of composed functions.-- (Note that they're not technically composed, they're curried.)main = do putStr "\n" putStr$ show $func1 3 2 putStr "\n" putStr$ show $func2 62 where func1 x y = x + y func2 = func1 35-- Example of passing a function, combined with an anonymous functionmain = sequence$ map (print . func2) list       where list = [1,2,3]-- Since I suppose the previous didn't really show a lambda expression,-- I guess I'll use this example insteadmain = sequence $map (\x -> (print (func2 x))) list where list = [1,2,3] The only things that might be confusing are the application operator ($), which only exists to change the precedence, and the sequence function, which takes a sequence of IO actions (from the print function), and constructs a larger IO action (main) from sequencing them in order.

Python versions of these are even more straightforward:
def main():    def func1(x,y): return x+y    print '\n',func1(3,2),def main():    def func1(x,y): return x+y    print '\n',func1(3,2),    func2 = lambda x: func1(35, x)    print '\n',func2(62),import sysdef main():    list = [1,2,3]    map (lambda x: sys.stdout.write('\n' + x), list)

The only reason sys.stdout is necessary is because print statements are invalid inside a lambda, so I just used the standard output (which is where print always prints anyway, and is redirectable) file instead.

Share on other sites
ruby is not C like either, but i recommend a look at it anyway.

languages like C/C++ don't usually support closures because they are very expensive in non-interpretted langauges, lots of item copying, and possibly different behaviors anyway when it comes time to actually use the object (because of the eternal "no such thing as an infinitely deep copy")

Share on other sites
I guess I should clarify...my reasons for wanting something C++ code-compatible are:

1) All code I've written thus far is in C++. Rewriting would be much too time consuming. This might be mitigated by being able to link my existing code with another language.

2) I'm working on a 3d game/simulation engine. I might be wrong, but doesn't the speed requirement rule out interpretted languages such as Ruby?

3) I like many features of the C++ language (especially multiple inheritance of interfaces and templates), and I'd like to continue using them.

So in the best case for me there'd be some magical preprocessor that accepts:

SuperC++
void main(){int func(int x) { return x * x; } int List[3] = {1, 2, 3}for_each(List, List+3, cout<<func(arg1) ); }

and converts it to:

C++
int anon_func_1 (int arg1) { return arg1*arg1; } struct anon_functor2 : public unary_function<int, void>{  void operator() (int arg1) { cout << anon_func_1(arg1);  }};void main(){int List[3] = {1, 2, 3}for_each (List, List+3, anon_functor2); }

But I guess such a preprocessor doesn't exist...so oh well...

The syntax looks terrifying but I'll check it out. How easily could I link C++ and haskell code? (Could I write an engine partially in C++ and partially in haskell and compile the two together?)

Re Python: Looks much friendlier than Haskell. Same question about linkage...can my existing C++ code be integrated?

Re function-pointers: Ummm...no...

Re C-Preprocessor macros: Not powerful enough to accomplish what I want.

So I guess it boils down to: Can a functional language make use of my object-oriented imperative code, and can it run at a speed acceptable for a game engine?

-Alex

Share on other sites
Quote:
 Original post by cypherxI guess I should clarify...my reasons for wanting something C++ code-compatible are: 1) All code I've written thus far is in C++. Rewriting would be much too time consuming. This might be mitigated by being able to link my existing code with another language.

Quote:
 2) I'm working on a 3d game/simulation engine. I might be wrong, but doesn't the speed requirement rule out interpretted languages such as Ruby?
So you'd use Python in limited circumstances. Remember, "Premature optimization is the root of all evil." -- Knuth

Quote:
 3) I like many features of the C++ language (especially multiple inheritance of interfaces and templates), and I'd like to continue using them.
The features of Haskell can not be directly compared with C++ (Haskell supports something like both of these, but type classes and polymorphic types have their own strengths and weaknesses). Python's dynamic typing and reflection/introspection is far more powerful than C++'s templates. Multiple inheritance works just fine in Python.

Quote:
 Re Haskell:The syntax looks terrifying but I'll check it out. How easily could I link C++ and haskell code? (Could I write an engine partially in C++ and partially in haskell and compile the two together?)
See for yourself. In general, I would expect exporting (using haskell code in other languages) to be easier than importing (using foreign code in Haskell). You may also want to look at this, which gives a fuller account of what happens.

Quote:
 Re Python: Looks much friendlier than Haskell. Same question about linkage...can my existing C++ code be integrated?
Python is relatively easy to integrate with C code. See Boost::Python

Quote:
 So I guess it boils down to: Can a functional language make use of my object-oriented imperative code, and can it run at a speed acceptable for a game engine?
Well, you can check it out at The Great Programming Language Shootout. Haskell is listed as "ghc" in the list. Python is listed as itself. And yes, both can make use of your existing code.

Share on other sites
I checked out the Great Programming Language Shootout. Very interesting to see how the functions are implemented in different languages.
From the bunch, I found Lua and Python to have a very clean and appealing syntax. Unfortunately, their benchmark peformance was horrible, orders of magnitude worse than C++.

Once I've solidifed the core of my engine, I think I'll expose it via Boost::Python or LuaBind and finish the program in-script. I can probably tolerate the inefficiency on lightweight code that only runs a few times a second.

Still, has anyone ever seen C++ extensions of the sort I mentioned before, or know of a simple way to create a FunctionalC++ to C++ source converter?

-Alex

[Edited by - cypherx on January 6, 2005 12:07:59 AM]

Share on other sites
Quote:
 Original post by FrunyClosures are extremely powerful constructs.

While I don't exactly disagree, I feel that this discussion is relevant.

Generally speaking, what seems lacking in C++ (and to an only slightly lesser extent Java) is the ability to express constructs anonymously. On the one hand, this is indeed rather annoying at times because of the amount of typing you need to "frame" a class, compared to what it does - if you indeed are creating a non-polymorphic class with just the one method, you may well find yourself wishing for a closure instead. OTOH, not associating a "name" to something makes it that much more difficult to use more than once.

FWIW, I find when I write Python that I will use anonymous lists and dicts all over the place (including list comprehensions; but that's a whole other story), lambdas rather less often, and nested functions/classes hardly ever. I'd gladly give up the ability to nest 'def/class' in exchange for lambdas that exec'd rather than eval'ing (i.e. allowed for statement use instead of just expressions).

What I'm getting at in a roundabout way is why I don't think Lisp will ever take over: all of the ideas are great for specific situations, but dealing with all the ideas all the time (especially when one of those ideas is the 'syntax-less' parse-tree syntax) is quite intimidating. The great thing about Python is that you can throw in a list comprehension or a stock library HOF like map or reduce into something that's otherwise pretty normal and OO (or even procedural), and just keep going. :)

(And yes, I *have* been reading Paul Graham's essays recently; why do you ask? ;) )

Share on other sites
Quote:
 Original post by ZahlmanWhat I'm getting at in a roundabout way is why I don't think Lisp will ever take over: all of the ideas are great for specific situations, but dealing with all the ideas all the time (especially when one of those ideas is the 'syntax-less' parse-tree syntax) is quite intimidating. The great thing about Python is that you can throw in a list comprehension or a stock library HOF like map or reduce into something that's otherwise pretty normal and OO (or even procedural), and just keep going. :)(And yes, I *have* been reading Paul Graham's essays recently; why do you ask? ;) )
I really don't understand what you're trying to say here. Can you give an example to help?

Share on other sites
I can blatantly steal Paul Graham's example and add my own commentary [grin]:

Quote:
 Teh Pual zomg!1Appendix: PowerAs an illustration of what I mean about the relative power of programming languages, consider the following problem. We want to write a function that generates accumulators-- a function that takes a number n, and returns a function that takes another number i and returns n incremented by i.(That's incremented by, not plus. An accumulator has to accumulate.)In Common Lisp this would be (defun foo (n) (lambda (i) (incf n i)))and in Perl 5, sub foo { my ($n) = @_; sub {$n += shift} }which has more elements than the Lisp version because you have to extract parameters manually in Perl.In Smalltalk the code is slightly longer than in Lisp foo: n |s| s := n. ^[:i| s := s+i. ]because although in general lexical variables work, you can't do an assignment to a parameter, so you have to create a new variable s....If you try to translate the Lisp/Perl/Smalltalk/javascript code into Python you run into some limitations.... This is what you end up with: def foo(n): s = [n] def bar(i): s[0] += i return s[0] return barPython users might legitimately ask why they can't just write def foo(n): return lambda i: return n += i or even def foo(n): lambda i: n += i and my guess is that they probably will, one day. (But if they don't want to wait for Python to evolve the rest of the way into Lisp, they could always just...)In OO languages, you can, to a limited extent, simulate a closure (a function that refers to variables defined in enclosing scopes) by defining a class with one method and a field to replace each variable from an enclosing scope. This makes the programmer do the kind of code analysis that would be done by the compiler...Python experts seem to agree that this is the preferred way to solve the problem in Python...In the rivalry between Perl and Python, the claim of the Python hackers seems to be that that Python is a more elegant alternative to Perl, but what this case shows is that power is the ultimate elegance: the Perl program is simpler (has fewer elements), even if the syntax is a bit uglier.How about other languages? In the other languages mentioned in this talk-- Fortran, C, C++, Java, and Visual Basic-- it is not clear whether you can actually solve this problem....It's not literally true that you can't solve this problem in other languages, of course. The fact that all these languages are Turing-equivalent means that, strictly speaking, you can write any program in any of them. So how would you do it? In the limit case, by writing a Lisp interpreter in the less powerful language.That sounds like a joke, but it happens so often to varying degrees in large programming projects that there is a name for the phenomenon, Greenspun's Tenth Rule: Any sufficiently complicated C or Fortran program contains an ad hoc informally-specified bug-ridden slow implementation of half of Common Lisp.

Now then. This sort of thing is a pet example for Lisp advocates because it's exactly the sort of thing where Lisp shines. To make a similar object in more traditional languages requires a lot more boilerplate-flavour code, and once you have set up something that seems like a reasonable facsimile, the Lisper may then insist that you also make it behave in the way the you get for free(TM) with dynamic typing.

Now then. For things which really are this simple, there is a lot to be said for doing it in Lisp... but I can't personally think in that way long enough, quickly enough to do anything really useful entirely in such a language (TBCH, I haven't actually used Lisp - just Scheme, which most would probably consider close enough).

What Python offers is the ability to ability to toss in little idiomatic things with the overall effect of a 'lambda' (and Python does offer lambdas, but for technical reasons that Mr. Graham references and I elided, they're not powerful enough for the example) or a 'map' or 'reduce', while still keeping a familiar looking structure to the program. By "structure" I mean both the static structure (how the code appears and is formatted) and dynamic structure (control flow). Even recursion is a difficult concept for many (although I suspect it just isn't taught very well), and you just can't hack Lisp if you don't grok that fully.

Basically I am not sold on the idea that "power is the ultimate elegance"; at some point readability and comprehensibility suffer as you make things more terse, and IMHO Python frequently finds itself near the top of that curve. When I translate Python to Java, I frequently find myself wondering why I need to add so many lines that aren't really making things any clearer (to be fair, most of these are close braces on lines by themselves ;) ) - but I don't find myself making significant changes to code structure, or wondering how to translate it. Were I to prototype in Lisp and try to translate that into an Algol-family language, I think I'd abandon all hope pretty quickly.

Python can't replace Java for me (and if I were in an environment where I had to use C++ for things, it wouldn't be able to replace C++ for me) - but I don't think I'll ever go back to Perl for anything now (although I can think of a few useful things I may want to implement in Python to give slightly more Perl-like behaviour...), and I'm not about to try to do anything serious in Lisp any time soon.