c preprocessor - Conditional Compilation in C for getting different versions of one function -
i asked myself if there nice way different versions of 1 function without copying whole source code. have different versions 1 one hand measuring execution time , on other hand writing intermediate result (for analytic purpose).
let me explain example: writing iterative solver solving linear system, in case gmres algorithm. measure runtime in 1 run , write residual @ every iteration step in different run. structure of program looks like:
//main.c #include "gmres.h" // setup arrays flag_print_res = 0; gmres(a,x,b,tol,maxtiter,flag_print_res); // reset arrays flag_print_res = 1; gmres(a,x,b,tol,maxtiter,flag_print_res); //..
and function:
//gmres.c void gmres(double* a, double *x, double *b, double tol, int maxiter, int flag_print_res) { // … start_time = ((double) clock ())/clocks_per_sec; for(iter=0; iter<maxiter; iter++) { // ... if(flag_print_res) { fprintf(file, "%d %13.5e\n", iter, res); } // ... } end_time = ((double) clock ())/clocks_per_sec; printf("execution time = %13.5e\n", end_time-start_time); }
however, problem execution time is, evaluation if statement costs time, specially when evaluated every iteration. therefore question: how can create different version of function, in if statement evaluated @ compile time?
you can control settings @ compile time macros. used make 1 version of function available in build, however.
if want have both versions available in same program, must implement 2 different versions of function. preprocessor can avoid duplicating code.
multiple inclusion of separate implementation
one approach implement function in separate file gmres.c
:
#ifndef gmres #error "gmres not defined" #endif void gmres(double* a, double *x, double *b, double tol, int maxiter) { (iter=0; iter < maxiter; iter++) { // ... #ifdef gmres_print fprintf(file, "%d %13.5e\n", iter, res); #endif // ... } }
and include file 2 sets of macros:
#define gmres gmres // plain version #include "gmres.c" #undef gmres #define gmres gmres_print // printing version #define gmres_print // set print flag #include "gmres.c" #undef gmres
although #include
commonly used include header files, can used include file, , here makes sense include *.c
file. make sure don't have #pragma once
or similar setting active. #error
directive ensures name of function given macro.
compile separate objects
the same can achieved compiling same source file 2 separate objects:
cc -dgmres=gmres -o gmres.o -c gmres.c cc -dgmres_print -dgmres=gmres_print -o gmres_print.o -c gmres.c cc -o gmres gmres.o gmres_print.o ...
here, versions controlled via build control tool, e.g. makefile. must make sure there no duplicate symbols.
cheap template
another approach "cheap template" implement whole function macro:
#define gmres_impl(name, flag) \ void name(double* a, double *x, double *b, double tol, int maxiter) \ { \ (iter=0; iter < maxiter; iter++) \ { \ // ... \ \ if (flag) fprintf(file, "%d %13.5e\n", iter, res); \ \ // ... \ } \ }
and implement 2 versions of different parameters:
gmres_impl(gmres, 0) gmres_impl(gmres_print, 1)
you cannot have preprocessor directives in macro, control must via macro arguments. compiler can evaluate constant expressions if (0)
, if (1)
, eliminate dead code accordingly.
this approach may have uses, has 1 major drawback: error messages , debugging symbols refer line function implementation evoked gmres_impl
, makes finding errors hard.
Comments
Post a Comment