The preprocessor is written in Perl. It converts generic files (files suffixed by .cct or
.ht) files into C++ file. Pratically, it is called directly by the
Makefile
. To convert a .cct file into
.cpp file or a file
.ht into file
.h the
Makefile
uses the commands:
perl -Ietc/macros etc/macros/template.pl etc/macros/pand_macros file.cct > file.cpp perl -Ietc/macros etc/macros/template.pl etc/macros/pand_macros file.ht > file.h
where etc/macros
directory is a subdirectory of the Pandore
directory.
The preprocessor recognizes lines beginning by `##'. Lines beginning with `##;' are considered as preprocessor comments and are discarded from the generated C++ file.
.cct file respects more or less the following template file:
/* -*- mode: c++; c-basic-offset: 3 -*- * * Copyright (c) 2013, GREYC. * All rights reserved * * You may use this file under the terms of the BSD license as follows: * * "Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of the GREYC, nor the name of its * contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." * * For more information, refer to: * https://clouard.users.greyc.fr/Pandore/ */ #include <pandore.h> using namespace pandore; ##begin Operator(TYPE1, VOISS) Errc Operator( const TYPE1 &ims, TYPE1 &imd, Short nbvois ) { return SUCCESS; } ## append loadcases if (objs[0]->Type() == Po_$TYPE1) { TYPE1 *const ims = (TYPE1*)objs[0]; objd[0] = new TYPE1(ims->Props()); TYPE1 *const imd = (TYPE1*)objd[0]; result = Operator(*ims, *imd, (Float)atof(parv[0])); } else ## end ##end ##; Generates Operator for Img1d<T>, Img2d<T> and Img3d<T>. ##forall(Operator, /Img.d/) ##; Generates Operator for Reg2d and Reg3d ##forall(Operator, /Reg[23]d/) #ifdef MAIN #define USAGE "usage: %s connexity [-m mask] [im_in|-] [im_out|-]" #define PARC 1 #define FINC 1 #define FOUTC 1 #define MASK 0 ##main(PARC, FINC, FOUTC, MASK, USAGE) #endif
The template file is composed of three parts:
##begin
and ##end
macros.##forall
macro;##main
macro.##begin
and ##end
macros is duplicated as many times as it is instantiated from Pandore objects. For example, the code can be instantiated from Img2duc, Graph3d, Reg2d, ... In order to adapt the code from the various types of Pandore objects, the begin
macro has some parameters that are set during instanciation. These parameters defined things like dimension, connexity, point and available loops from the type of the Pandore object. For example, if the Pandore type, is a Img2duc
then, the dimension is Dimension2d, the point is Point2d and the loop is a double for loop.The declaration of a generic function looks like the following command -A variable is included only if it is used in the body of the function:
##begin Operator( TYPE, VOISL, POINT, DIM, LOOPP )
Dimension1d
, Dimension2d
, Dimension3d
].Point1d
, Point2d
, Point3d
]. POINT p;
##LOOPP(ims,p)
Point2d p; for (p.y=0; p.y<ims.Height(); p.y++) for (p.x=0; p.x<ims.Width(); p.x++)
POINT p;
##LOOPPIN(ims,p)
for (p.y=ims.Height()-1; p.y>=0; p.y--) for (p.x<ims.Width()-1; p.x>=0; p.x--)
POINT p;
##LOOPPB(ims,p,5)
Point2d p; for (p.y=5; p.y<ims.Height()-5; p.y++) for (p.x=5; p.x<ims.Width()-5; p.x++)
POINT p;
##LOOPPINB(ims,p,5)
Point2d p; for (p.y=ims.Height()-5-1; p.y>= 5; p.y--) for (p.x=ims.Width()-5-1; p.x>= 5; p.x--)
For example:
##begin Operator( TYPE, POINT, DIM, VOISL, LOOPP ) Errc Operator(TYPE &ims) { POINT p; Float sum=0.0F; DIM d; d=ims.Size(); TYPE imd; imd.New(d); ##LOOPPB(ims,p,5) // Inner loop { for ( v=0; v<VOISL; v++ ) // VOISL can be 8 for 2D or 26 for 3D. sum += ims[p+v$VOISL[v]) } imd[p]=sum/VOISL; imd.Frame(ims,1); return SUCCESS; }
Errc Operator(Img2duc &ims) { Point2d p; Float sum=0.0F; Dimension2d d; d=ims.Size(); Img2duc imd; imd.New(d); for (p.y=5; p.y<ims.Height()-5; p.y++) for (p.x=5; p.x<ims.Width()-5; p.x++) { for ( v=0; v<8; v++ ) sum += ims[p+v8[v]) } imd[p]=sum/8; imd.Frame(ims,1); return SUCCESS; }
$
can be used to separate a preprocessor variable from other words. For instance, the word Po_TYPE
must be rewritten as Po_$TYPE
to be instantiated as Po_Imx3dsf if TYPE
is Imx3dsf
.append
macro is used to add a case in the main()
switch. All the variables defined in the parameter list of the begin macro and the variables defined in the main function can be used in the code between ##append
and ##end
macros. Most of the append contents look like the following code: ##begin Operator( TYPE,...., LOOPPB, POINT ) .... ## append if (objs[0]->Type()==Po_$TYPE) { TYPE *const ims=(TYPE)objs[0]; objd[0]=new TYPE(ims->Props()); TYPE *const imd(TYPE)obd[0]; result=Operator(*ims,*imd,(TYPE::ValueType)atof(parv[0])); } ## end ##end
if (objs[0]->Type()==Po_Imx3dsf) { Imx3dsf *const ims=(Imx3dsf)objs[0]; objd[0]=new Imx3dsf(ims->Props()); Imx3dsf*const imd(Imx3dsf)obd[0]; result=Operator(*ims,*imd,(Imx3dsf::ValueType)atof(parv[0])); } ## end ##end
##forall
is used to generate the list of operator functions from the list of the chosen types. The parameters are the type of Pandore objects. ##forall(Operator,/type1/, /type2/, /type3/, ...)
##forall(Operator,/Imc[23]duc/,/Img2ds/)
##Operator(imc2duc, Img2dsl) ##Operator(imc2duc, Img2dsf) ##Operator(imc3duc, Img2dsl) ##Operator(imc3duc, Img2dsf)
The easier way to parametrize this macro is to define constants as follows:
#ifdef MAIN #define USAGE "USAGE: %s connexity [-m mask] [im_in|-][im_out|-]" #define PARC 1 #define FINC 1 #define FOUTC 1 #define MASK 3 ##main(PARC,FINC,FOUTC,MASK,USAGE) #endif
The contents of the macro ##append is added to the main()
in the switch structure.
#include <pandore.h> ##; Images ##begin Add(IMG1, IMG2, VARS, LOOPP, POINT) Errc Add(IMG1 &ims1, IMG2 &ims2, Select<IMG1,IMG2>::Signed &imd) { POINT p; ## LOOPP(ims1,p) imd[p]=(Select<IMG1,IMG2>::Signed::ValueType)((ims1[p]+ims2[p])/2); return SUCCESS; } ## append loadcases if ( ( objs[0]->Type() == Po_$IMG1 ) && ( objs[1]->Type() == Po_$IMG2 ) ) { IMG1* const ims1=(IMG1*)objs[0]; IMG2* const ims2=(IMG2*)objs[1]; objd[0]=new Select<IMG1,IMG2>::Signed(ims1->Size()); Select<IMG1,IMG2>::Signed*const imd=(Select<IMG1,IMG2>::Signed*)objd[0]; result=Add(*ims1,*ims2,*imd); } else ## end ##end ##; Graphs ##begin AddGraph(TYPE) Errc Add(TYPE &gs1,TYPE &gs2,TYPE &gd) { int i; gd.Init(gs1); for (i=1;i<=gd.size;i++) if ((g[i])) gd[i]->attr=((gs1[i]->attr+gs2[i]->attr)/2.0F); return SUCCESS; } ## append loadcases if ( ( objs[0]->Type() == Po_$TYPE ) && ( objs[1]->Type() == Po_$TYPE ) ) { TYPE* const gs1=(TYPE*)objs[0]; TYPE* const gs2=(TYPE*)objs[1]; objd[0]=new TYPE(gs1->Size()); TYPE* const gd=(TYPE*)objd[0]; result=Add(*gs1,*gs2,*gd); } else ## end ##end ##forall(Add,Img2d,Img2d) ##forall(Add,Img3d,Img3d) ##forall(AddGraph,Graph) #ifdef MAIN ##main(0,2,1,1,"USAGE: %s [-m mask] [im_in1|-] [im_in2|-] [im_out|-]") #endif