4

I'm working on a C++ maths library in which I want to be able to configure at compile time using defines.

One of the configurations is defining the precision. In code it looks like this:

#ifdef MYMATH_USE_DOUBLE
    typedef double Real;
#else
    typedef float Real;
#endif

That works fine.

If someone wants to then use the library after it has been configured with MYMATH_USE_DOUBLE they'll have to also pass that define to the compiler.

Is there a better way of doing this?

I don't want the user to have to remember what defines were used to compile the maths librarys and then repeat them all for their app.

5 Answers 5

2

I would suggest using templates, with double as default.

template <typename F = double>
F sin(const F& r)
{
//...
}

That way users can use the functions as is for doubles, but they have the option of changing the type:

float f = sin<float>(r);

EDIT: The template system should auto-infer that F is a float in this case though, given r is a float.

Sign up to request clarification or add additional context in comments.

5 Comments

The nice part of using templates is that it also allows the user of the library to use custom floating types for your functions.
I would use templates (it does solve the problem), but I have concerns about using them on base classes in the library. I'm making a matrix/quat/vector for 3D apps. I trying to avoid using templating classes like a 4x4 Matrix classes which would in theory be used alot.
How come you are avoiding templating those classes? If you make sure the functions accept const references then you're still generating efficient code. If you're doing some heavy matrix transformations, then passing them in by reference (regardless of the type) would be a good idea.
Hmm. I guess the main template problem I've been warned about (exploding compile times) won't really matter, since the user will probably only use one or two types of the class.
Templates do cause compile time to increase, but it's nowhere near "exploding". Long compile times can be solved in many different ways anyways, and you shouldn't worry about it as a library designer. If the user wants to use parallel-make, or put his code that uses your library in a different module, that's his decision.
2

Provide two parallel sets of functions, one for the implementation using float and the other for the implementation using double (and a third for long double). This is what the C library does - there is sin() for double, sinf() for float, and sinl() for long double.

Or, in C++, you could (probably should?) consider using overloads or templates. My suspicion is, though, that that It might lead to confusion rather than simplicity (or, preponderantly, it will use the double overloads since floating-point literals are double unless suffixed explicitly), but templates are often the method of choice these days.

(Comments modified in the light of comments by bstamour; I was being too conservative and 1990s-ish.)

1 Comment

Since this is C++, you might as well use templates, as this kind of problem is what they're designed to solve. Plus the template system can infer the templated type based on the type of the parameter passed, so it shouldn't lead to confusion from the users' perspective.
1

Usually, the best practice is to run "configure" script, that creates one file with all defines. And this file is included in all headers. For example, if you compile OpenSSL from sources, "configure" creates e_os.h (as far as a remember the name), that is included practically in every header.

2 Comments

This is what I thought would be the solution. Now I just need to find out if my build-tool-of-choice (scons) has way to build the header.
This answers how to compile preferences into a library. I accept that using templating (described in the other comments) would also work but require more code refactoring.
1

As others already sugggested, using templates is a possible solution to your problem. However, if you expose your generic code to users, they'll have to recompile your library when choosing a different type. It might make more sense to only use generic code internally and expose your interface with a fixed set of types using plain-old function overloading:

// generic implementation (internal linkage):
namespace {
    template<typename Real>
    Real plus42(Real value) {
        return value + 42;
    }
}

// API functions (external linkage):
float plus42(float value) { return plus42<>(value); }
double plus42(double value) { return plus42<>(value); }

Assuming the GNU toolchain, you should be able to avoid pulling in dead code when linking statically by passing -fvtable-gc -ffunction-sections -fdata-sections to the compiler and -Wl,--gc-sections to the linker.

Comments

0

Put the conditional define in the header files of your library (if it's not already there) and place a compiler-search directive in the appropriate lib-file along with it (when it's included by clients).

#ifdef MYMATH_USE_DOUBLE
    typedef double Real;  
$ifndef _LIB // only for clients
#pragma comment( lib, "double_lib" ) // double_lib name of the library. 
#endif
#else 
    typedef float Real;  
$ifndef _LIB
#pragma comment( lib, "float_lib" ) 
#endif
#endif  

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.