I first did this for every single C++ primitive type, all the way from bool to long long & long double, both signed and unsigned not using templates. This generated a 25k line header about half a megabyte in size.
The mess can be seen here
The written code in this example, while is nearly identical, instead generates template vectors which result in an output a small fraction in size of the non-templated. The code generated in this example can be seen here vec1, vec2, vec3, and vec4 or here. The written code can be seen below.
//file: main.cpp
#include "JMeta/Function.h" //need to make a common header, "JMeta/Function.h" coincidentally #includes all we need
using namespace JMeta; //everything in this C++ generator exists within namespace JMeta
//==================================================================================================
enum ComponentSet { X, XY, XYZ, XYZW};
//`````````````````````````````````````````````````````````````````````````````````````````````````
Class *scalarTemplateOf(ComponentSet); //generates a template class of either vec1, 2, 3, or 4
//==================================================================================================
int main (int, char **)
{
//loop through component sets and make a vector for each
for(uint i = 0, z = XYZW+1; i<z; ++i)
{
File(scalarTemplateOf(ComponentSet(i))).save();
}
return 0;
}
//==================================================================================================
//These enums are simply used as unsigned integer ids and are not at all visible to the framework
//``````````````````````````````````````````````````````````````````````````````````````````````````
enum ConstructID //Keep track of constructor IDs allowing them to be called later
{
EachComponent
Vector2Vector2,
ComponentArray,
DefaultConstructor,
SingleElementConstructor,
CopyConstructor,
PointerConstrucor
};
enum SectionID { //keep track of class sections, these are used as integer ids to refer to class/struct divisions
ConstructorSection,
MemberAccessSection,
SubscriptSection,
BeginEndMethodSection,
StepOperatorSection,
BinaryOperatorSection,
SSBinaryOperatorSection,
OtherOperatorSection,
FunctionSection,
ArraySection,
MemberSection
};
//==================================================================================================
Class *scalarTemplateOf(ComponentSet cs)
{
const static QString name_max {"xyzw"};
uint count = cs+1;
auto xyz_name = name_max.mid(0, count);
auto vecx = new Class("vec"+numberToText(count), StructureType::Struct); //the heap is currently the primary allocation source :(
auto t = vecx->newTemplateType(0, "T"); //put typename of "T" into the template header, (with an id of 0) & retrieve
auto vecT = vecx->selfSpecialized(); //vecx specialized with its own template parameter(s), only "T" for this
auto constRefT = t.toConstReference(); //const reference of "T"
//array data types of type "T" some methods will need to use
const ArrayDataType *arrays[] = {
t->toArray(1u),
t->toArray(2u),
t->toArray(3u),
t->toArray(4u)
};
auto arrayt = arrays[cs]; //array the size of this vector
//calling: void Class::appendNewSection(AccessSpecifier access, uint id)
vecx->appendNewSection(AccessSpecifier::Public, ConstructorSection); //constructor section
vecx->appendNewSection(AccessSpecifier::Public, MemberAccessSection); //member access section
vecx->appendNewSection(AccessSpecifier::Public, SubscriptSection); //subscript operator section
vecx->appendNewSection(AccessSpecifier::Public, BeginEndMethodSection); //begain & end section
vecx->appendNewSection(AccessSpecifier::Public, FunctionSection); //function section
vecx->appendNewSection(AccessSpecifier::Public, StepOperatorSection); //step operators section
vecx->appendNewSection(AccessSpecifier::Public, BinaryOperatorSection); //binary operators section
vecx->appendNewSection(AccessSpecifier::Public, SSBinaryOperatorSection); //single scalar binary operators section
vecx->appendNewSection(AccessSpecifier::Public, OtherOperatorSection); //misc operators section
vecx->appendNewSection(AccessSpecifier::Public, ArraySection); //array stuff section
vecx->appendNewSection(AccessSpecifier::Private, MemberSection); //object section, need to rename
//Add object Members & their "get" (or reference) and "set" access methods
for( uint i = 0, z = count; i<z; ++i) {
QChar n = name_max[i];
vecx->newObject(t, QString(n)+"v", MemberSection)->setId(i); //object member
auto f = vecx->newObjectFunction(n, i, MemberAccessSection);//object reference returning method
f->cloneConstant(); //cv qualified reference returning method
//setX, Y, etc...)
f = vecx->newFunction(Void, QString("set")+n.toUpper(), MemberAccessSection);
f->signature()->append(constRefT, n, 0);
f->appendStatement(BinaryFunction::call(constRefT, BinaryType::Equals, vecx->object(i), f->parameterExpression(0)));
}
//add default constructor which will foward all memders 0 via initialization list
vecx->newConstructor(DefaultConstructor, ConstructorSection)->forwardMembers(0);
//add single element constructor
if (count>1){
auto fc = vecx->newConstructor(SingleElementConstructor, ConstructorSection);
fc->addModifier(Constructor::Modifier::Explicit); //make this explicit of course
fc->signature()->append(constRefT, xyz_name, 0); //add parameter of the given type and name, with an id of 0
fc->forwardMembersParameter(0); //forward to all members parameter of id 0
}
{ //pointer constructor, example: inline float4(float *);
auto fc = vecx->newConstructor(PointerConstrucor, ConstructorSection);
fc->addModifier(Constructor::Modifier::Explicit); //make this explicit as well
fc->signature()->append(t.toConstPointer(), "begin", 0); //name the pointer parameter
for(uint i = 0; i<count; ++i) //using square bracket on the passed pointer to initialize members
fc->appendMemberInitialization(i, MemberAccessFunction::subscriptCall(constRefT, fc->parameterExpression(0), express(i, UInt)));
}
//Add constructor requesting a parameter for each member and simply forward via initialization list
vecx->newMemberForwardingConstructor(EachComponent, ConstructorSection);
//add copy constructor
if(count>1) vecx->newPerMemberCopyConstructor(CopyConstructor, ConstructorSection);
//add construction from 2 array[2]s for vec4 type
if(count==4) {
auto fc = vecx->newConstructor(Vector2Vector2, ConstructorSection);
fc->signature()->append(arrays[1]->toConstReference(), "xy", 0);
fc->signature()->append(arrays[1]->toConstReference(), "zw", 1);
for(uint i = 0; i<count; ++i)
fc->appendMemberInitialization(i, MemberAccessFunction::subscriptCall(constRefT, fc->parameterExpression(i<2?0:1), express(i<2?i:i-2, UInt)));
}
//begin method, first part of making this vector usable with the ranged for-loop
auto fbegin = vecx->newFunction(t->toPointer(), "begin", vecx->object(X)->pointer(), BeginEndMethodSection);
fbegin->cloneConstant(); //make a cv-qualified version
auto fcount = vecx->newFunction(UInt, "count", express(count, UInt), SubscriptSection); //returns vector component count
fcount->addModifier(FunctionModifier::Const); //cv qualified obviously
auto begincall = fbegin->call(NO_ARGS);
{
//add end method making this vector usable with the ranged for-loop | this will call begin + count
auto e = vecx->newFunction(t->toPointer(), "end", BeginEndMethodSection);
e->appendReturn(BinaryFunction::call(fbegin->returnType(), BinaryType::Addition, begincall, fcount->call(NO_ARGS)));
e->cloneConstant(); //cv-version
}
{
vecx->setOpenSection(SubscriptSection); //tell Class "vecx" to put all new stuff in "SubscriptSection"
//add subscript operator
auto so = new BracketFunction(t->toReference(), BracketType::Subscript, vecx); //operator[] method
so->signature()->append(UInt, "i", 0);
so->appendStatement(expressAssert(BinaryFunction::call( //Assert on out of range index
Bool, BinaryType::LessThan, so->parameterExpression(0), fcount->call(NO_ARGS)), expressText("Index out of range!")));
so->appendReturn(BinaryFunction::call(t.toPointer(), BinaryType::Addition, begincall, so->parameterExpression(0))->reference());
so->cloneConstant(); //add cv-qualified version
}
vecx->setOpenSection(StepOperatorSection); //tell Class "vecx" to put all stuff new stuff in "StepOperatorSection"
//step operators
for(uint bt = 0; bt<uint(StepType::PostDecrement)+1; ++bt)
{
StepType b = StepType(bt);
auto rt = commonReturnFor(b, vecT);
auto bf = new StepFunction(rt, b, vecx);
if(rt==vecT.toReference()) { //self modifying (e.g. ++i)
for(uint i = 0; i<count; ++i)
bf->appendStatement(StepFunction::call(rt, b, vecx->object(i)));
bf->appendReturn(expressThisRef(vecx));
}
else if(rt==vecT){//copy, modify, return old copy (e.g. i++)
ArgumentList args;
for(uint i = 0; i<count; ++i)
args.append(i, StepFunction::call(rt, b, vecx->object(i)));
bf->appendReturn(vecx->constructor(EachComponent)->call(args, t));
}
}
//add binary & unary methods
auto vecRef = vecT.toConstReference();
vecx->setOpenSection(BinaryOperatorSection);
for(uint bt = 0; bt<(uint)BinaryType::LogicalAnd; ++bt){ //iterating through binary operators
BinaryType b= BinaryType(bt); //binary operator enumerant
{
auto rt = commonReturnFor(b, vecT); //common return given for this operator given the left parameter assuming the right alike
auto bf = new BinaryFunction(rt, b, vecx, vecRef->forParameter(0, xyz_name));//scaler to scaler binary (e.g. vec3* vec3 )
if(rt==vecT.toReference()) { //self modifying (e.g. +=, -=, %=)
for(uint i = 0; i<count; ++i)
bf->appendStatement(BinaryFunction::call(t, b, vecx->object(i), bf->parameterExpression(0)->object(i)));
bf->appendReturn(expressThisRef(vecT));
}
else if(rt==vecT){//copy return (e.g. a+b, a-b, a%b)
ArgumentList args;
for(uint i = 0; i<count; ++i)
args.append(i, BinaryFunction::call(t, b, vecx->object(i), bf->parameterExpression(0)->object(i)));
bf->appendReturn(vecx->constructor(EachComponent)->call(args, t));
bf->addModifier(FunctionModifier::Const);
}
else if(rt==Bool) {
ArgumentList args;
for(uint i = 0; i<count; ++i)
args.append(i, BinaryFunction::call(t, b, vecx->object(i), bf->parameterExpression(0)->object(i)));
bf->appendReturn(BinaryFunction::call(rt, (b==BinaryType::NotEqualTo||b==BinaryType::LogicalOr)?
BinaryType::LogicalOr : BinaryType::LogicalAnd, args));
bf->addModifier(FunctionModifier::Const);
}
}
}
vecx->setOpenSection(SSBinaryOperatorSection); //single scalar binary (e.g. vec3 * 5.0f)
for(uint bt = 0; bt<(uint)BinaryType::LogicalAnd; ++bt){
BinaryType b= BinaryType(bt);
if(isMultiplicationOrDivision(b)||isModulus(b))
{
auto rt = commonReturnFor(b, vecT);
auto sbf = new BinaryFunction(rt, b, vecx, t->forParameter(0, xyz_name)); //single scaler binary (e.g. vec3*= float)
if(rt==vecT.toReference()) { //self modifying (e.g. +=, -=, %=)
for(uint i = 0; i<count; ++i)
sbf->appendStatement(BinaryFunction::call(t, b, vecx->object(i), sbf->parameterExpression(0)));
sbf->appendReturn(expressThisRef(vecT));
}
else if(rt==vecT){//copy return (e.g. a+b, a-b, a%b)
ArgumentList s_args;
for(uint i = 0; i<count; ++i)
s_args.append(i, BinaryFunction::call(t, b, vecx->object(i), sbf->parameterExpression(0)));
sbf->appendReturn(vecx->constructor(EachComponent)->call(s_args, t));
sbf->addModifier(FunctionModifier::Const);
}
}
}
static UnaryType utypes[] = {UnaryType::Negative, UnaryType::Positive, UnaryType::Not};
vecx->setOpenSection(OtherOperatorSection);
for(auto u : utypes)
{
auto rt = commonReturnFor(u, vecT);
auto uf = new UnaryFunction(rt, u, vecx);
if(rt==vecT.toReference()) { //self modifying
for(uint i = 0; i<count; ++i)
uf->appendStatement(UnaryFunction::call(t, u, vecx->object(i)));
uf->appendReturn(expressThisRef(vecx));
}
else if(rt==vecT){//copy return
ArgumentList args;
for(uint i = 0; i<count; ++i)
args.append(i, UnaryFunction::call(t, u, vecx->object(i)));
uf->appendReturn(vecx->constructor(EachComponent)->call(args, t));
uf->addModifier(FunctionModifier::Const);
}
else if(u==UnaryType::Not) {
ArgumentList args;
for(uint i = 0; i<count; ++i)
args.append(i, UnaryFunction::call(t, u, vecx->object(i)));
uf->appendReturn(BinaryFunction::call(Bool, BinaryType::LogicalAnd, args));
uf->addModifier(FunctionModifier::Const);
}
}
//array stuff
for( uint i = 1, z = count; i<z; ++i)
{ //conversion to & from array types of lesser components
//constructors
if(i>1){
auto ac = vecx->newConstructor(AutoID, ConstructorSection);
ac->signature()->append(arrays[i-1]->toConstReference(), name_max.mid(0, i), 0);
for(uint k = i; k<count;++k)
ac->signature()->append(constRefT, QChar(name_max[k]), k);
ArgumentList args;
for(uint a = 0; a<count; ++a)
args.append(a, a<i?MemberAccessFunction::subscriptCall(t, ac->parameterExpression(0), express(a, UInt)):ac->parameterExpression(/*a+1-i*/a));
ac->delegateTo(EachComponent, args);
}{
//functions
auto a = arrays[i];
auto n = name_max.mid(0, i+1);
auto f = vecx->newFunction(a->toReference(), n, ArraySection);
f->appendReturn(Function::cast(Reinterpret, a->toPointer(), begincall)->reference());
//cannot use "cloneConstant" because the cv-version's return statement is different & must be defined explictly
auto cf = vecx->newFunction(a->toConstReference(), f->name(), ArraySection); //cv - version
cf->addModifier(FunctionModifier::Const);
cf->appendReturn(Function::cast(Reinterpret, a->toConstPointer(), begincall)->reference());
auto sf = vecx->newFunction(Void, "set"+n.toUpper(), cf->returnType().forParameter(0, "them"), ArraySection);
sf->appendStatement(Function::call(Void, "memcpy", {f->call(NO_ARGS), sf->parameterExpression(0u), expressSizeOf(a)}));
/*sf->appendStatement(Function::call(Void, "memcpy", {f->call(NO_ARGS).staticCast(voidPointer()),
sf->parameterExpression(0u).staticCast(constVoidPointer()), expressSizeOf(a)}));*/
}
}
if(count>0) { //array reference methods
auto a = vecx->newFunction(arrayt->toReference(), "array", ArraySection);
a->appendReturn(Function::cast(Reinterpret, arrayt->toPointer(), begincall)->reference());
auto ac = vecx->newFunction(arrayt->toConstReference(), "array", ArraySection);
ac->appendReturn(Function::cast(Reinterpret, arrayt->toConstPointer(), begincall)->reference());
ac->addModifier(FunctionModifier::Const);
// implicit array conversion
vecx->newAliasOperatorFunction(a->call(NO_ARGS), ArraySection)->cloneConstant();
}
auto sf = vecx->newFunction(Void, "to"); //convert to other vector type of the same component count
auto t1 = sf->newTemplateType(0u, "T1");
auto vecxt1 = vecx->specialized(t1);
sf->setReturn(vecxt1);
auto args = vecx->membersToArgs();
for(auto &a : args)
a = a.staticCast(t1);
sf->appendReturn(vecx->constructor(EachComponent)->call(args, t1));
{// array constant refernce constructor
auto fc = vecx->newConstructor(ComponentArray, ConstructorSection);
fc->signature()->append(arrayt->toConstReference(), "that", 0);
for(uint i = 0; i<count; ++i)
fc->appendMemberInitialization(i, MemberAccessFunction::subscriptCall(constRefT, fc->parameterExpression(0), express(i, UInt)));
}
//inline all methods
{
auto fs = vecx->allMembersOf<AbstractFunction>();
for(AbstractFunction *f : fs) {
f->makeInline(); //puts definition lower in header
//f->inlineLocally(); //puts definition in class
//f->removeModifier(FunctionModifier::Inline); //puts definitions in .cpp file
}
}
return vecx;
}