Zlodo {l Wrote}:You were judging you were an expert from your own clearly limited experience. 3 years is not enough to become an expert at any language and therefore to be able to judge yourself whether you are an expert, much less 8 languages.
I mastered them in 3 and have been applying them for well over 8 after that. I was also judging from the half dozen or so programming tournaments I have participated in since, not to mention my experience with other developers online, and in those from my current (and previous) employment(s).
Zlodo {l Wrote}:Or perhaps they prefer to avoid doing this kind of things because it frankly sound like an awful idea.
See, that's a prime example of how people are plagued by conventional knowledge. Their too stuck on what society teaches them instead of pushing the boundaries and expanding. Please don't make me list whats wrong with this (Bill Gates, Einstein, Galileo, etc).
Zlodo {l Wrote}:It doesn't solve the biggest problems with the standard C preprocessor
It doesn't? You could have fooled me, as I certainly don't have to deal with most of those issues (not even the debugging one). The features I have implemented are very useful, and actually fixes many of the issues the C macro system has. In fact, and just for an example, debugging is actually much easier than traditional C.
Zlodo {l Wrote}:but it's a source of headache that shouldn't be done lightly.
And you say I lack experience? Rofl, this stuff is the easiest crap on earth, which is why I do it on a whim. Why don't you try writing a ping-pong game in Scheme, then you will know what a REAL headache is. Oh, and anyone who has issues with C-macros are just too inexperienced to use them correctly

.
I guarantee you have never seen code like mine before

. Take a look at a quick test-case scenario (which demonstrates the most basic features):
- {l Code}: {l Select All Code}
// The extra functionality I have added allows the macros to literally write code in a Scheme-like fashion:
// __PREPROCESSOR_OUTPUT__ is the output this macro produces that is substituted wherever this macro goes (if no reference to this is made the output of a macro behaves like a normal C-macro)
// ##= is a operation like += instead performing the ## equivalent
// #foreach will loop through each preprocessor variable and store it in the requested ident
// #for works like a C for loop
// #local_define defines a variable locally inside the macro (and sub-macros called within that macro). It does not leave the scope of the parent macro.
// #typeof is replaced by the type of of its argument (if it is already a type it returns the type)
// NOTE: this is only the tip of the iceberg :D
// used to create unique identifiers for constructors, much the way C++ works under the hood.
#define OBJECT_CONSTRUCTOR_IDENT(objectType, ...) \
__PREPROCESSOR_OUTPUT__ = objectType ## _constructor_;
#foreach(__VA_ARGS__, arg) __PREPROCESSOR_OUTPUT__ ##= #typeof(arg) ## _;
// creates the unique identifier of a constructor for automaticaly calling while hidding in standard C syntax
// the object is passed via the first variable called object
#define defineConstructor(objectType, ...) OBJECT_CONSTRUCTOR_IDENT(objectType, __VA_ARGS__)(objectType * object, __VA_ARGS__)
// allocates a object by a perticular constructor (note: this version unrolls the loop--using sparingly and beware big executables!)
#define allocObject(objectType, count, ...) \
objectType * objects = calloc(number, sizeof(objectType)); \
#for(0, count, i) { \
OBJECT_CONSTRUCTOR_IDENT(objectType, __VA_ARGS__)(&objects[i], ...); \
} \
__PREPROCESSOR_OUTPUT__ = objects;
// a wrapper to allocObject() to mimic C++
#define new(objectType, count, ...) allocObject(objectType, count, ...)
typedef struct someStruct_t {
s32 someValue;
} someStruct;
void defineConstructor(someStruct, s32 someValue) {
object->someValue = someValue;
}
s32 main(s32 argc, c8 ** argv) {
// allocates 10 someStruct's, passing the value 5 to the appropriate constructor
someStruct * test = new(someStruct, 10, 5);
printf("%d\n", test[4].someValue);
return 0;
}
That implements a memory allocation system similar to C++, but is much faster (performance AND dev-time wise). It is also far more versatile. For example, in a single new() call I could pass separate someValue arguments to each constructor, which is impossible to do in C++ without workarounds (allocate an array of pointers to objects, and manually allocate each object). I could even call different constructors AND use different parameters for each in a single new() call--try and do that in C++

. The unimaginably great potential found in this basic example alone shows just useful it is.
For another example consider debugging. After expansion the code looks like this: (see code just bellow). That is the code the compiler ends up with, as all the macros are handled in a pre-compile hook. Because everything is written in so simple terms debugging is a cinch--it in no way suffers from tradition C-macro issues with regards to bugs and/or debugging. Do note the debugging comments it adds to make things even easier.
- {l Code}: {l Select All Code}
// Original code
typedef struct someStruct_t {
s32 someValue;
} someStruct;
/*
Expanded macro 'defineConstructor' called on line 46 in 'main.c'.
Original:
defineConstructor(someStruct, s32 someValue)
Output:
OBJECT_CONSTRUCTOR_IDENT(someStruct, s32 someValue)(someStruct * object, s32 someValue)
Expanded macro 'OBJECT_CONSTRUCTOR_IDENT' called on line 1 of macro 'defineConstructor' in 'main.c'.
Original:
OBJECT_CONSTRUCTOR_IDENT(someStruct, s32 someValue)(someStruct * object, s32 someValue)
Output:
someStruct_constructor_s32(someStruct * object, s32 someValue)
*/
void someStruct_constructor_s32(someStruct * object, s32 someValue) {
object->someValue = someValue;
}
s32 main(s32 argc, c8 ** argv) {
/*
Expanded macro 'new' called on line 52 in 'main.c'.
Original:
new(someStruct, 10, 5);
Output:
allocObject(someStruct, 10, 5)
Expanded macro 'allocObject' called on line 1 of macro 'new' in 'main.c'.
Original:
allocObject(someStruct, 10, 5);
Output:
someStruct * objects = calloc(10, sizeof(someStruct));
#for(0, 10, i) {
OBJECT_CONSTRUCTOR_IDENT(someStruct, __VA_ARGS__)(&objects[i], __VA_ARGS__);
}
__PREPROCESSOR_OUTPUT__ = objects;
Expanded macro 'OBJECT_CONSTRUCTOR_IDENT' called on line 3 of macro 'allocObject' in 'main.c'.
Original:
OBJECT_CONSTRUCTOR_IDENT(someStruct, 5)(&objects[i], __VA_ARGS__);
Output:
someStruct_constructor_s32(&objects[i], __VA_ARGS__);
*/
someStruct * test = NULL;
{
someStruct * objects = calloc(10, sizeof(someStruct));
someStruct_constructor_s32(&objects[0], 5);
someStruct_constructor_s32(&objects[1], 5);
someStruct_constructor_s32(&objects[2], 5);
someStruct_constructor_s32(&objects[3], 5);
someStruct_constructor_s32(&objects[4], 5);
someStruct_constructor_s32(&objects[5], 5);
someStruct_constructor_s32(&objects[6], 5);
someStruct_constructor_s32(&objects[7], 5);
someStruct_constructor_s32(&objects[8], 5);
someStruct_constructor_s32(&objects[9], 5);
test = objects;
}
// Original code
printf("%d\n", test[4].someValue);
return 0;
}
In fact, I would argue it is easier to debug than C++ code. Consider this example: Each and every function (including constructors) have a unique identifier because its required in C. This means you know exactly what constructor/function is being called, whereas in C++ function mismatching can happen all the time because it does not require unique identifiers--you could accidentally be calling the wrong constructor because of a inline conversion operator, or the countless other situations where this could occur. On the flip side of the coin in my C+Scheme hybrid this will never happen.
Back to the original topic--this is just a very very very small taste of the amazing functionality wrought by a pre-compile hook. And even that is just an unimaginably small chunk of the vast benefits that could be gained by learning multiple languages (its just one example amongst dozens). IMHO every programmer would benefit astronomically from learning multiple languages. If not for the experience value, than the marketability it adds to your resume

.