3/13/2009

03-13-09 - Automatic Prefs

I wrote briefly at molly about the way I do "hot vars" or "tweak vars". I don't really like them. I had a bunch in my GDC app for tweaking, but it means I have to edit the code to tweak things and I don't like that for various reasons. It's way better to have a real text pref file. There are just so many wins. I can source control it seperately. I can copy it off and save good snapshots of prefs I like for different purposes, like a development prefs vs. final run prefs. I can copy it and change it to make new instances. And I can edit it and have my final app load the changes without a recompile (hot var can do this too if you keep the C file around as "data")

Fortunately I have a prefs system in cblib, so I switched to that. But it made me realize that I'd really like to have an automatic pref system. Basically I want to write something like :


struct MyPref
{

int i = 7;
float x = 1.3;
String str = "hello world";
ColorDW color(200,50,177);
Vec3 v(3.4,0,1.7);

};

and have it automatically get IO to the pref file and construction with those values as defaults.

Now, that all is actually pretty easy if I just make some custom syntax and run a source code preprocess over the file before compiling. I could use a syntax like :


struct MyPref
{

int i; //$ = 7;
float x; //$ = 1.3;
String str; //$ = "hello world";
ColorDW color; //$ (200,50,177);
Vec3 v; //$ (3.4,0,1.7);

};

where anything with a //$ automatically becomes a "pref var" that gets IO'd and tweakability and so on.

The annoyance and something I haven't figured out is just how to deal with generated code in a build setting. Should the code generator stick the generated code back into the original file? Should it make another seperate C file on the side and put the generated code in there? Maybe all the generated code from all the C files in the whole project should go together in one big spot?

I dunno but it seems like a mess. Maybe the easiest thing to do would be to put the autogenerated code in the same file, and run the generator as a pre-build step.

It would also be annoying to have to put the code generator in as a pre-build step on every file one by one. So annoying as to be unacceptable actually. I would want it to automatically run on all my files any time I build, so that if I just go and put the autogen markup in any file it does the stuff and I don't have to open msdev options dialogs.

I know people out there are doing things like this, so I'm curious how you deal with the mod time issues and builds and source control.

BTW the autogenerated code will look something like this :


void MyPref::AutoGen_SetDefault()
{
    i = int(7);
    x = float(1.3);
    str = String("hello world");
    color =  ColorDW(200,50,177);
    v = Vec3(3.4,0,1.7);
}

template <typename functor>
void MyPref::AutoGen_Reflection()
{
    REFLECT(i);
    REFLECT(x);
    REFLECT(str);
    REFLECT(color);
    REFLECT(v);
}

pretty easy to generate just by some text movement. The big win of the shortened syntax is that you only have to write a variable once instead of 3 times.

6 comments:

castano said...

That's why I like cmake, it can generate projects automatically, so that you don't have to edit project settings manually.

For example, for Qt apps that need the Qt objects to be preprocessed you would write:

qt4_wrap_cpp(preprocessed_files ${files_to_preprocess})
add_executable(name ${other_files} ${preprocessed_files})

Here we add the preprocessed files to the build. A common alternative is to include them from the cpp file:

#include "preprocessed_file.moc"

Another option is to use a different extension for the files that you want to have preprocessed, and create a custom rule for them, but that may not work on older version of visual studio.

nothings said...

Yeah, makefiles are the big win for this. I have a few generated .c files in g, and they're all specified manually since I couldn't find a way to automate it in VC6, but fortunately those aren't c files, just special source files, so there's only a few. Doing it for every C file would be a huge pain.

You don't want the autogenerator to touch the original file since that fucks with timestamps, and you don't want all the autogenerated code to go in one file because that fucks with incrementality, so one output file per input file is probably the big win. (I actually generate a .c, a .h, and a .inl from my thing.)

Brian said...

If you have the autogenerated code placed in the original file, it will make a mess of your version control. Why not just #include the autogenerated file into your files where you need them?

Justin Paver said...

As a way to solve this problem, I always thought it would be nifty to extend the C preprocessor to be struct/class aware. eg.

void MyPref::NotAutoGen_Reflection()
{
#for_each_member_variable(M, MyPref)
REFLECT(M);
#end_for
}

cbloom said...

"If you have the autogenerated code placed in the original file, it will make a mess of your version control. Why not just #include the autogenerated file into your files where you need them?"

Yeah, I think that's the way to go. For xxx.cpp I can include xxx.aug ; in fact seeing a cpp that includes an aug could be the trigger to run the processor.

There's another question of whether the augs should be checked in or if they should be treated like "obj" files and remade on each machine.

cbloom said...

"As a way to solve this problem, I always thought it would be nifty to extend the C preprocessor to be struct/class aware. eg."

Yep, that is one of the few enhancements to C++ I've always wanted. If they add member iteration, typeof, and the ability to construct from a type_info (like new(type_info)) we'd be set.

old rants