Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best way to build variants of the same C/C++ application

I have three closely related applications that are build from the same source code - let's say APP_A, APP_B, and APP_C. APP_C is a superset of APP_B which in turn is a superset of APP_A.

So far I've been using a preprocessor define to specify the application being built, which has worked like this.

// File: app_defines.h
#define APP_A 0
#define APP_B 1
#define APP_C 2

My IDE build options then specify (for example)

#define APPLICATION APP_B

... and in source code, I will have things like

#include "app_defines.h"

#if APPLICATION >= APP_B
// extra features for APPB and APP_C
#endif

However, I shot myself in the foot this morning and wasted far to much time by simply omitting the line to #include "app_defines.h" from one file. Everything compiled fine, but the application crashed with AVs at startup.

I'd like to know what a better way of handling this would be. Previously, This would normally one of the few times when I'd consider #define could be used (in C++, anyway), but I still goofed up badly and the compiler didn't protect me.

like image 268
Roddy Avatar asked Nov 04 '08 13:11

Roddy


4 Answers

You don't always have to force inheritance relationships in applications that share a common code base. Really.

There's an old UNIX trick where you tailor the behavior of you application based on argv[0], ie, the application name. If I recall correctly (and it's been 20 years since I looked at it), rsh and rlogin are/were the same command. You simply do runtime configuration based on the value of argv[0].

If you want to stick with build configuration, this is the pattern that is typically used. Your build system/makefile defines a symbol on the command like, APP_CONFIG to be a non-zero value then you have a common include file with the configuration nuts and bolts.

#define APP_A 1
#define APP_B 2

#ifndef APP_CONFIG
#error "APP_CONFIG needs to be set
#endif

#if APP_CONFIG == APP_A
#define APP_CONFIG_DEFINED
// other defines
#endif

#if APP_CONFIG == APP_B
#define APP_CONFIG_DEFINED
// other defines
#endif

#ifndef APP_CONFIG_DEFINED
#error "Undefined configuration"
#endif

This pattern enforces that the configuration is command line defined and is valid.

like image 111
plinth Avatar answered Oct 13 '22 19:10

plinth


What you are trying to do seems very similar to "Product lines". Carnigie Melon University has an excellent page on the pattern here: http://www.sei.cmu.edu/productlines/

This is basically a way to build different versions of one piece of software with different capabilities. If you imagine something like Quicken Home/Pro/Business then you are on track.

While that may not be exactly what you attempting, the techniques should be helpful.

like image 32
Jere.Jones Avatar answered Oct 13 '22 18:10

Jere.Jones


It sounds to me that you might look at modularizing your code into separately-compiled elements, building the variants from a selection of common modules and a variant-specific top-level (main) module.

Then control which ones of these parts go into a build by which header files are used in compiling the top level and which .obj files you include into the linker phase.

You might find this a bit of a struggle at first. In the long run you should have a more reliable and verifiable construction and maintenance process. You should also be able to do better testing without worrying about all the #if variations.

I'm hoping that your application is not terribly large just yet and unraveling a modularization of its functions won't have to deal with a big ball of mud.

At some point you might need run-time checks to verify that the build used consistent components for the application configuration you intended, but that can be figured out later. You can also achieve some compile-time consistency checking, but you'll get most of that with header files and signatures of entry points into the subordinate modules that go into a particular combination.

This is the same game whether you are using C++ classes or operating pretty much at the C/C++ common-language level.

like image 2
orcmid Avatar answered Oct 13 '22 18:10

orcmid


If you're using C++, shouldn't your A, B, and C applications inherit from a common ancestor? That would be the OO way to solve the problem.

like image 1
JesperE Avatar answered Oct 13 '22 19:10

JesperE