Skip to main content
gamedev.net
🔒 Locked

Code that writes code (C++)

Started by The Parrot Jun 30, 2006 at 3:04 PM 5 replies 1.5k views
Original Post
The Parrot
The Parrot
Hello! Recently I've been wondering; is it possible (within reasonable efford spans) to make code that writes code in C++? For instance; I could have a series of commands stored in a series of classes which point to eachother in a tree-branch way. 10 classes could draw a triangle, 10 others a square, all with unique variables etc. To call this, the software has to refer through all the classes, whilst essentially the thing being drawn could stay the same each frame. At first I thought of 'collapsing' the tree, and making every command in one list. Then I reasoned; why keep a list? Is it possible to let the program write a function that will call all the functions I stored? For instance, if a class calls the function "DrawTriangle(x, y)", x and y being class variables, it could write a command that calls DrawTriangle and 'bake' it's class x and y: if x = 10 and y = 20, it writes the command "DrawTriangle(10, 20);". I'd reserve a buffer capable of storing all the commands, fool my program into thinking it's a function, and BANG, it works or things go kabloo'ey :) This kind of cache'ing commands could be a speed gain, but I'm more interested in the technical side of it than in the practical side of it for now. So :) any thoughts?
--Jeroen Stout - WebsiteCreator of-Divided
Extrarius
Extrarius
Quote:
Original post by The Parrot
[...]Is it possible to let the program write a function that will call all the functions I stored?[...]
Yes. However, it involves writing the backend of a compiler (the part that turns things from an internal representation to object code), which is a HUGE project if you know nothing about that kind of thing (and not much smaller if you do). If you _really_ wanted, you could just output the code to a cpp file, generate the neccessary header, and then have your program call GCC or the like to compile the code into a dynamic link library, which your program could then load and use.
It's probably better to just leave it as a kind of interpreter, though you can probably significantly improve the design of that part of your system (might want to look int using an existing scripting language such as Lua)
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
Zahlman
Zahlman
The only languages where this is practical and portable are those where the overhead imposed by the language is likely to dwarf any performance gains you could get by doing it. :( The thing is, if x and y are anything but compile-time constants, chances are they change enough that you can only be safe by re-generating the code each time, and that will take *much* more time than simply taking the hit for the function call.

However, when they *are* compile-time constants, you can always take advantage of compile-time information in order to trade off code space for speed, by using templates as a code generation technique.

If you're just wondering in general, then no, you can't get something for nothing like this.

If you're wondering because your renderer is slow, then there is almost certainly a higher-level issue preventing you from getting the speed you want.
The Parrot
The Parrot
I'm wondering in general; there's no code holding me back. When I read some random threads here I often hear posters asking how to change such low-level issues whilst the bottleneck is higher up, so I know I shouldn't worry because of that :)

Just to make the point clear; this all is just part of my 'lab'-part of C++: experimental nonsense! Fun experimental nonsense, to be exact.

x and y would not be compile time constants; my hypothetical user would, during runtime, set the variables in the command list, which would then 'batch' it to the buffer, turning it into a function. So creating a cpp file out of it is not really an option; the user could perform an action that drastically changes the way the program works, during run-time, and the program re-batching the new workflow into that command list.

I'm glad it *was* hypothetical, seeing as the buzz already is turning dark :)

Thanks for your replies, though! So fast I barely had hit F5 :)
--Jeroen Stout - WebsiteCreator of-Divided
jkleinecke
jkleinecke
A more practical way would be to use templates. I've seen some templates that can reduce sine and cosine functions at compile time.
-----------------------------------Indium Studios, Inc.
MaulingMonkey
MaulingMonkey
"Code writing code" is otherwise known as metaprogramming. C++'s metaprogramming featurs are mostly constrained to templates and the preprocessor. A snippet from the boost library collection's metaprogramming utilities:

Template Metaprogramming
  • mpl - Template metaprogramming
    framework of compile-time algorithms, sequences and metafunction classes,
    from Aleksey Gurtovoy.
  • static_assert
    - Static assertions (compile time assertions), from John
    Maddock.
  • type_traits -
    Templates for fundamental properties of types, from John
    Maddock, Steve Cleary, et al.

Preprocessor Metaprogramming
  • preprocessor - Preprocessor
    metaprogramming tools including repetition and recursion, from Vesa Karvonen
    and Paul Mensonides.


It sounds like, however, that you're describing something easily achieved with the Command Pattern. While it's not geared towards the creating of actual functions, it is useful for calling pre-existing functions. Here's a very basic example using the utilities in the boost library collection:

void foo( void ) {    std::cout << "Foo" << std::endl;}void bar( int x , int y ) {    std::cout << "Bar(" << x << "," << y << ")" << std::endl;}void baz( std::string text ) {    std::cout << "Baz(" << text << ")" << std::endl;}std::list< boost::function< void ( void ) > > commands;commands.push_back( boost::bind( &bar , 1 , 2 ) );commands.push_back( & foo );commands.push_back( boost::bind( & baz , "I like pie" ) );//run our command list:std::for_each( commands.begin() , commands.end() , std::mem_fun( & boost::function< void ( void ) >::operator () ) );//will call bar(1,2) , foo() , and baz( "I like pie" ) in that order.
Ravyne
Ravyne
Code which generates code is is useful for a number of things. Most common are tools which generate code off-line to be integrated into a project and compiled by the user. An example of this is the tools which read in a graphic file and convert it to a byte array. This is how Graphics are included in GB/GBA games for instance, because there's no file system or resources like we have in the PC world.

In real-time, there are a few notable examples of its utility as well, primarily in optimized software renderers. The traditional fixed-function graphics pipeline, at least conceptually, is a state machine. Rather than managing a state through varius flags checked at runtime (lots of if statements, which can be bad for cache misses, pipeline stalls, etc.) it is possible to generate code representing the exact state of the current settings. When something in the state changes, the code must be regenerated, but because there is much more processing done than state-changing it works out to be a speed advantage. Most of these systems work by defining a const function pointer of a certain call signature and point it at a buffer of memory. The code generater then outputs machine code directly into this buffer based upon the state it is being fed and the characteristics of the call signature. To the program, there is no difference between this generated function and any other function. Several current software renderes use this technique or some variation of it. Pix-o-matic (software renderer used in the Unreal Engines,) vincent (an OpenGL|ES renderer for mobile devices and PCs) and SwiftShader (The author is here on these forums) are the ones I can name off the top of my head.

Its also worth noting that the .NET platform and languages have the ability to generate .Net code on the fly through some of the standard libraries.
throw table_exception("(? ???)? ? ???");

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.