Sunday, October 16, 2016

Code to Generate vec1, 2, 3, & 4


In this example, generated are vector structures, not like std::vector, but the types used commonly in graphics development (e.g. DirectX, OpenGL) to store vertex positions, surface normals, texture coordinates, etc... 

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;
   }



As stated above, this code generates vec1, vec2, vec3, and vec4

Pages