Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Support for naming library variants and testing all valid possibilities

CXXD offers support, based on the mod headers which are being included, in the form of macros for naming a library variant and for testing all valid variant possibilities. These macros are meant to be used as an adjunct to the functionality of non-header only libraries where we want to name a library variant and create preprocessor error messages for library variants which we do not support. However the macro for testing variant possibilities has its use in header only libraries also, so these macros deserve a topic of their own.

These macros can be used to provide a more automatic methodology for accomplishing the goals which can be found in the manual methods employed in the MyLibName.hpp sample headers specified previously. It is completely up to the end-user of CXXD whether he finds it easier to use the macros which I will describe or whether he finds it easier to use the manual methods previously explained and illustrated.

Both macros I will be discussing use the mod-IDs which have been previously explained, for identifying the various mods in CXXD.

In additional a further identifier of 'CXXD_MODS_ALL' is used in each macro. The CXXD_MODS_ALL identifier refers to all mods in slightly different ways in the two macros which follow.

Macro naming a library variant

There is a macro in CXXD for naming a library variant based on the dual library chosen for the mods being included when the macro is actually invoked. This macro provides an automatic methodology for naming which can replace a manual method the end-user may have chosen to use. The macro is a variadic macro whose name and signature is:

CXXD_LIBRARY_NAME(...)

The macro is in the header file <boost/cxx_dual/library_name.hpp>

The macro takes one or more variadic parameters. The single required variadic parameter is the base name of the library. The remaining variadic parameters are optional and are Boost PP tuples. Given the required base name of a library parameter and the optional remaining variadic parameters the macro expands to a library variant name.

Each Boost PP tuple optional parameter has one to three elements.

  1. The first element,which is mandatory, is a mod-ID or the CXXD_MODS_ALL identifier. The mod-ID represents the mod which is being referenced, while the CXXD_MODS_ALL identifier represents all mods.
  2. The second optional element, which may be empty, is a preprocessor identifier to be appended to the base name of the library if the particular mod header, for the mod being referenced, is included and the C++ standard library has been chosen as the dual library. If the first element is CXXD_MODS_ALL then this parameter is appended to the base name only if all included mod headers have their C++ standard library chosen.
  3. The third optional element, which may be empty, is a preprocessor identifier to be appended to the base name of the library if the particular mod header, for the mod being referenced, is included and the Boost library has been chosen as the dual library. If the first element is CXXD_MODS_ALL then this parameter is appended to the base name only if all included mod headers have their Boost library chosen.

If the first element mod identifier of the Boost PP tuple is not recognized, the entire tuple is just ignored, rather than having any sort of preprocessor error generated.

An examples of an optional parameter:

(CXXD_REGEX,stdrx,boostrx)
// Append 'stdrx' to the base name if the C++ standard regex library has been chosen
// Append 'boostrx' to the base name if the Boost regex library has been chosen

(CXXD_TUPLE,tupstd)
// Append 'tupstd' to the base name if the C++ standard tuple library has been chosen
// Append nothing to the base name if the Boost tuple library has been chosen

(CXXD_RATIO,,_br)
// Append nothing to the base name if the C++ standard ratio library has been chosen
// Append '_br' to the base name if the Boost ratio library has been chosen

(CXXD_MODS_ALL,_std,_boost)
// Append '_std' to the base name if the C++ standard regex library has been chosen for all mod headers
// Append '_boost' to the base name if the Boost regex library has been chosen for all mod heaaders
// If either of the above cases is true, all other optional parameters are ignored

The optional parameters allow the macro invoker to specify his own identifiers to be appended to the base library name for each mod header being included. If he does not specify his own identifier for a particular mod by using an optional variadic parameter, defaults values are chosen instead.

The default values to be appended to the base name for each mod, depending on whether that mod uses the C++ standard library or the Boost library, are given in the following table:

Table 1.7. Library name defaults

Mod ID

C++ standard

CXXD_ARRAY

_ar

CXXD_ATOMIC

_at

CXXD_BIND

_bd

CXXD_CHRONO

_ch

CXXD_CONDITION_VARIABLE

_cv

CXXD_ENABLE_SHARED_FROM_THIS

_es

CXXD_FUNCTION

_fn

CXXD_HASH

_ha

CXXD_MAKE_SHARED

_ms

CXXD_MEM_FN

_mf

CXXD_MOVE

_mv

CXXD_MUTEX

_mx

CXXD_RANDOM

_rd

CXXD_RATIO

_ra

CXXD_REF

_rf

CXXD_REGEX

_rx

CXXD_SHARED_MUTEX

_sm

CXXD_SHARED_PTR

_sp

CXXD_SYSTEM_ERROR

_se

CXXD_THREAD

_th

CXXD_TUPLE

_tu

CXXD_TYPE_INDEX

_ti

CXXD_TYPE_TRAITS

_tt

CXXD_UNORDERED_MAP

_um

CXXD_UNORDERED_MULTIMAP

_up

CXXD_UNORDERED_MULTISET

_ut

CXXD_UNORDERED_SET

_us

CXXD_WEAK_PTR

_wp

CXXD_MODS_ALL

_std


As can be seen in the default table above:

The 'CXXD_MODS_ALL' identifier specifies a single mnemonic, in place of all the individual mnemonics, to be added to the base name if all the mod headers being included have either their C++ standard chosen as the dual library or their Boost library chosen as the dual library. In the default case above if all the mod headers being included have the C++ standard library as the dual library the final library variant name is the base name of the library with '_std' appended, while if all the mod headers being included have the Boost library as the dual library the final library variant name is just the base name with nothing further appended. If all the mod headers being included have either their C++ standard chosen as the dual library or their Boost library chosen as the dual library then all individual mod mnemonics are completely ignored.

In the default case any appended mnemonics are added to the base name in the alphabetical order in which the individual mods occur in the default table above and not in the order in which the mods are included.

[Note] Note

The default appended mnemonics all begin with an underscore and lowercase letters so they should not clash with any macros of the same name. Nonetheless because there is a small chance of a macro clash if the compiler, or some other header file, defines macros with the same name as one of the default mnemonics, CXXD saves and restores possible macros definitions with the default appended mnemonics names. Saving is done automatically when the <boost/cxx_dual/library_name.hpp> is included. Restoring must be done manually by the end-user by including the header file <boost/cxx_dual/library_name_post.hpp> after the CXXD_LIBRARY_NAME macro is invoked. If you are sure that no other macro in the TU in which CXXD_LIBRARY_NAME is being used has the name of any one of the default appended mnemonics, you do not have to include <boost/cxx_dual/library_name_post.hpp> after the CXXD_LIBRARY_NAME is invoked. Defining macro names that are not all uppercase letters and/or that start with an underscore, such as the default appended mnemonics, is really bad macro design, but one never knows what compiler vendors or third-party libraries are capable of doing.

The way that the CXXD_LIBRARY_NAME works, when the single required base name is passed to the macro, is exactly the manual method I have previously used when specifying unique variant names in the MyLibName.hpp samples given above. I chose to illustrate the manual method in the documentation based on the way that the CXXD_LIBRARY_NAME macro is designed to work when no optional parameters are specified and the default values are chosen. But of course an end-user, either manually specifying unique library variant names instead of using the CXXD_LIBRARY_NAME macro, or using the CXXD_LIBRARY_NAME macro and providing his own appended mnemonics as optional parameters, can create any schema he wants to name a library variant.

The default naming of CXXD_LIBRARY_NAME guarantees that if Boost is consistently being chosen as the dual library instead of the equivalent C++ standard library, which is what will naturally happen in the vast majority of cases if C++11 mode is not being used during compilation, the library name generated by CXXD_LIBRARY_NAME is the same as the base name. This latter is, I believe, what Boost library authors would expect in their non-header only libraries.

The end-user changes the default naming scheme by passing as optional parameters one or more Boost PP tuples for each mod where he wants a different mnemonic to be appended to the base name than what the default mnemonic table offers. In this case the Boost PP tuple used as an optional parameter specifies a different mnemonic to be used for a particular mod, depending on whether the dual library to be chosen for that mod is the C++ standard library or the Boost library. The order of the mnemonics to be appended also changes according to the order of the mod identifiers he specifies in the optional parameters, so that optional parameter mnemonics always precede default mnemonic choices in the final expanded name.

Before using the CXXD_LIBRARY_NAME macro the programmer should include the mod headers for the mods he wants the macro to analyze, in order to produce the desired library variant name. Just as when we manually created out MyLibName.hpp we really only need the low-level mod implementation headers for the mods we want the CXXD_LIBRARY_NAME macro to analyze. For this reason our examples below include the low-level implementation headers for the mods being used. Of course if the programmer is already including the mod headers themselves in a TU in which he is invoking the CXXD_LIBRARY_NAME macro there is absolutely no reason to separately include the low-level implementation headers, since each mod header includes its own low-level mod implementation header.

A number of examples will be given of using the using CXXD_LIBRARY_NAME macro with the four mods we have previously chosen in our examples for MyLibName.hpp header file and some arbitrarily chosen combinations. These arbitrarily chosen combinations are specified purely to illustrate how the macro works:

#include <boost/cxx_dual/impl/array.hpp>
#include <boost/cxx_dual/impl/function.hpp>
#include <boost/cxx_dual/impl/regex.hpp>
#include <boost/cxx_dual/impl/tuple.hpp>
#include <boost/cxx_dual/library_name.hpp>

/* If array uses Boost, function uses C++ standard, regex uses C++ standard, tuple uses Boost then: */

CXXD_LIBRARY_NAME(MyLib) expands to 'MyLib_fn_rx'

/* If array uses C++ standard, function uses C++ standard, regex uses C++ standard, tuple uses C++ standard then: */

CXXD_LIBRARY_NAME(MyLib) expands to 'MyLib_std'

/* If array uses Boost, function uses Boost, regex uses Boost, tuple uses Boost then: */

CXXD_LIBRARY_NAME(MyLib) expands to 'MyLib'

/* If array uses Boost, function uses C++ standard, regex uses C++ standard, tuple uses Boost then: */

CXXD_LIBRARY_NAME(MyLib,(CXXD_REGEX,RStd,_rboost)) expands to 'MyLibRStd_fn'

/* If array uses Boost, function uses C++ standard, regex uses Boost, tuple uses Boost then: */

CXXD_LIBRARY_NAME(MyLib,(CXXD_TUPLE,_cpp_tuple,_boost_tuple),(CXXD_ARRAY,,_bboost)) expands to 'MyLib_boost_tuple_bboost_fn'

/* If array uses Boost, function uses Boost, regex uses Boost, tuple uses Boost then: */

CXXD_LIBRARY_NAME(MyLib,(CXXD_ALL,Std,Boost)) expands to 'MyLibBoost'

/* If array uses C++ standard, function uses C++ standard, regex uses C++ standard, tuple uses C++ standard then: */

CXXD_LIBRARY_NAME(MyLib,(CXXD_ALL,Std,Boost)) expands to 'MyLibStd'

If we look at the MyLibName.hpp as presented previously we can use the CXXD_LIBRARY_NAME to simplify the syntax of the file. In the first case, where we do not try to limit the variants, our file would be:

// Header file MyLibName.hpp
#include <boost/cxx_dual/impl/array.hpp>
#include <boost/cxx_dual/impl/function.hpp>
#include <boost/cxx_dual/impl/regex.hpp>
#include <boost/cxx_dual/impl/tuple.hpp>
#include <boost/cxx_dual/library_name.hpp>
#define MYLIB_LIBRARY_NAME CXXD_LIBRARY_NAME(MyLib)

In the second case where we do limit the variants so that mod array and mod function always choose the same dual library, our file would be:

// Header file MyLibName.hpp
#include <boost/cxx_dual/impl/array.hpp>
#include <boost/cxx_dual/impl/function.hpp>
#include <boost/cxx_dual/impl/regex.hpp>
#include <boost/cxx_dual/impl/tuple.hpp>
#include <boost/cxx_dual/library_name.hpp>
#if (CXXD_HAS_STD_ARRAY && !CXXD_HAS_STD_FUNCTION) || (!CXXD_HAS_STD_ARRAY && CXXD_HAS_STD_FUNCTION)
    #error MyLib - CXXD configuration not supported where array and function differ.
#else
    #define MYLIB_LIBRARY_NAME CXXD_LIBRARY_NAME(MyLib)
#endif

In the third case where we limit our variants to the two choices of all Boost or all C++ standard libraries, our file would be:

// Header file MyLibName.hpp
#include <boost/cxx_dual/impl/array.hpp>
#include <boost/cxx_dual/impl/function.hpp>
#include <boost/cxx_dual/impl/regex.hpp>
#include <boost/cxx_dual/impl/tuple.hpp>
#include <boost/cxx_dual/library_name.hpp>
#if (CXXD_HAS_STD_ARRAY && CXXD_HAS_STD_FUNCTION && CXXD_HAS_STD_REGEX && CXXD_HAS_STD_TUPLE) || \
    !(CXXD_HAS_STD_ARRAY || CXXD_HAS_STD_FUNCTION || CXXD_HAS_STD_REGEX || CXXD_HAS_STD_TUPLE)
    #define MYLIB_LIBRARY_NAME CXXD_LIBRARY_NAME(MyLib)
#else
    #error CXXD configuration only supported if all dual libraries are Boost or all dual libraries are C++ standard.
#endif

In all cases the invocation of the single CXXD_LIBRARY_NAME macro simplifies our MyLibName.hpp header file. Nonetheless the choice of using the CXXD_LIBRARY_NAME macro or manually name the library variants is totally up to the library author.

Notice above in our new 'MyLibName.hpp' example code that while the CXXD_LIBRARY_NAME macro simplifies our code very nicely, we still have all that ugly testing which we do with the elaborate '#if ...' type statements to limit the variants we want to use in our second and third situations. The next macro which CXXD supports is meant to eliminate such ugliness in favor of a syntactically easier solution.

Macro testing all possibilities

There is a macro in CXXD for testing whether the mod headers which are included, when the macro is actually invoked, match one or more combinations of dual library possibilities. This macro provides an automatic method for testing all dual library possibilities at once and offers an alternative to the manual testing of dual library possibilities which the end-user may have chosen to use with various '#if ...' type statements. The macro is a variadic macro whose name and signature is:

CXXD_VALID_VARIANTS(...)

The macro is in the header file <boost/cxx_dual/valid_variants.hpp>

The variadic parameters are ways of encoding the various CXXD dual library combinations which the library author considers valid for his library. If the dual libraries chosen for the mods being included match any of combinations represented by the variadic parameters the macro expands to 1, otherwise it expands to 0.

The macro is meant to replace the elaborate '#if ...' type statements in our 'MyLibName.hpp' exmaple code with a single invocation of:

#if CXXD_VALID_VARIANTS(some_input)
    #define MYLIB_LIBRARY_NAME CXXD_LIBRARY_NAME(MyLib)
#else
    #error MyLib - Some Error Message
#endif

Each variadic parameter represents a 'possibility'. Each possibility is a series of Boost PP tuples. This 'series' in the VMD library is called a VMD sequence.

Each Boost PP tuple in the series has two elements. The first Boost PP tuple element is a mod-ID or CXXD_MODS_ALL. The second Boost PP tuple element is either 0 or 1, meaning respectively that for the mod specified the dual library is Boost (0) or the dual library is the C++ standard library (1). If a mod-ID as the first tuple element is not recognized, or if the second tuple element is not 0 or 1, that tuple is ignored as part of a possibility but the possibility itself is still considered.

The Boost PP tuple encoding identifies a valid choice for the library being built for a single mod. The series of Boost PP tuples combine the valid choices into a single possibility. When a particular mod is not part of a possibility, as encoded by a mod-ID, it simply means that if that mod's header is included it can validly use either its Boost or C++ standard library as far as the possibility is concerned.

As an arbitrary example of a possibility:

(CXXD_REGEX,0)(CXXD_ARRAY,1)(CXXD_TUPLE,1)

as one of our variadic parameters says that a situation where the regex mod uses the Boost library while the array and tuple mods use the C++ standard library is a valid possibility for the library.

The library author can specify as many valid possibilities as he chooses as variadic parameters to the macro, but at least one possibility has to be specified to use the macro. If all possible combinations of the mods being included are valid there is absolutely no point in using the CXXD_VALID_VARIANTS macro as we have no intention of limiting the possible variants by using the macro.

For the CXXD_MODS_ALL identifier only one tuple should define the possibility. The designation as a variadic parameter of:

(CXXD_MODS_ALL,0)

says that a valid possibility is that every mod included uses Boost as the dual library. The designation as a variadic parameter of:

(CXXD_MODS_ALL,1)

says that a valid possibility is that every mod included uses the C++ standard library as the dual library. If the CXXD_MODS_ALL tuple is combined with other tuples as part of a possibility the other tuples are ignored.

Before using the CXXD_VALID_VARIANTS macro the programmer should include the mod headers for the mods he wants the macro to analyze, in order to produce the desired result. Just as when we manually created out MyLibName.hpp we really only need the low-level implementation headers for the mods we want the CXXD_VALID_VARIANTS macro to analyze. For this reason our examples below include the low-level implementation headers for the mods being used. Of course if the programmer is already including the mod headers themselves in a TU in which he is invoking the CXXD_VALID_VARIANTS macro there is absolutely no reason to separately include the low-level implementation headers, since each mod header includes its corresponding low-level mod implementation header.

A number of examples will be given using the four mods we have previously chosen in our examples for MyLibName.hpp header file and some arbitrarily chosen valid possibilities. These arbitrarily chosen valid possibilities are specified purely to illustrate how the macro works:

#include <boost/cxx_dual/impl/array.hpp>
#include <boost/cxx_dual/impl/function.hpp>
#include <boost/cxx_dual/impl/regex.hpp>
#include <boost/cxx_dual/impl/tuple.hpp>
#include <boost/cxx_dual/valid_variants.hpp>

/* Valid possibilities are:
1) All four mods use the Boost library
2) mod array uses the Boost library,
   mod function uses the C++ standard library,
   mod tuple uses the Boost library,
   mod regex uses either the Boost library or the C++ standard library
*/

CXXD_VALID_VARIANTS((CXXD_MODS_ALL,0),(CXXD_ARRAY,0)(CXXD_FUNCTION,1)(CXXD_TUPLE,0))

/* Valid possibilities are:
1) All four mods use the C++standard library
2) mod array uses the Boost library,
   mod function uses the Boost library
   mod tuple uses the Boost library or the C++ standard library,
   mod regex uses the Boost library or the C++ standard library
*/

CXXD_VALID_VARIANTS((CXXD_MODS_ALL,1),(CXXD_ARRAY,0)(CXXD_FUNCTION,0))

/* Valid possibilities are:
1) mod array uses the Boost library,
   mod function uses the Boost library
   mod tuple uses the Boost library or the C++ standard library,
   mod regex uses the Boost library or the C++ standard library
2) mod array uses the C++ standard library,
   mod function uses the C++ standard library
   mod tuple uses the Boost library or the C++ standard library,
   mod regex uses the Boost library or the C++ standard library
*/

CXXD_VALID_VARIANTS((CXXD_ARRAY,0)(CXXD_FUNCTION,0),(CXXD_ARRAY,1)(CXXD_FUNCTION,1))

/* Valid possibilities are:
1) All four mods use the Boost library
2) All four mods use the C++standard library
3) mod regex uses the Boost library
   mod tuple uses the C++ standard library
   mod array uses the Boost library or the C++ standard library,
   mod function uses the Boost library or the C++ standard library
*/

CXXD_VALID_VARIANTS((CXXD_MODS_ALL,0),(CXXD_MODS_ALL,1),(CXXD_REGEX,0)(CXXD_TUPLE,1))

Essentially the CXXD_VALID_VARIANTS present an alternate form of '#if (CXXD_HAS_STD_XXX ...)' preprocessor constructs which are used to limit the mod possibilities which a particular library decides is valid. If we take our example MyLibName.hpp which limit the possibilities of dual libraries combinations we can re-code our second and third case using this macro. Since our first case, which does not limit the possibilities, has no use for using the CXXD_VALID_VARIANTS macro, we need not illustrate its use with the macro.

In the second case where we do limit the variants so that mod array and mod function always choose the same dual library, our file would now be:

// Header file MyLibName.hpp
#include <boost/cxx_dual/impl/array.hpp>
#include <boost/cxx_dual/impl/function.hpp>
#include <boost/cxx_dual/impl/regex.hpp>
#include <boost/cxx_dual/impl/tuple.hpp>
#include <boost/cxx_dual/valid_variants.hpp>
#if CXXD_VALID_VARIANTS((CXXD_ARRAY,0)(CXXD_FUNCTION,0),(CXXD_ARRAY,1)(CXXD_FUNCTION,1))
    #define MYLIB_LIBRARY_NAME CXXD_LIBRARY_NAME(MyLib)
#else
    #error MyLib - CXXD configuration not supported where array and function differ.
#endif

In the third case where we limit our variants to the two choices of all Boost or all C++ standard libraries, our file would now be:

// Header file MyLibName.hpp
#include <boost/cxx_dual/impl/array.hpp>
#include <boost/cxx_dual/impl/function.hpp>
#include <boost/cxx_dual/impl/regex.hpp>
#include <boost/cxx_dual/impl/tuple.hpp>
#include <boost/cxx_dual/valid_variants.hpp>
#if CXXD_VALID_VARIANTS((CXXD_MODS_ALL,0),(CXXD_MODS_ALL,1))
    #define MYLIB_LIBRARY_NAME CXXD_LIBRARY_NAME(MyLib)
#else
    #error CXXD configuration only supported if all dual libraries are Boost or all dual libraries are C++ standard.
#endif

In both cases we produce shorter versions of our MyLibName.hpp code.

Using CXXD_VALID_VARIANTS for a header-only library

Even though there is little practical benefit of limiting possibilities of mod dual library choices when the situation is a header only library as opposed to the non-header only library, the CXXD_VALID_VARIANTS macro could be used in that situation also. As an example let us suppose we have a header-only library which uses the array mod and function mod along with other mods. As we have done in one of our non-header only MyLibName.hpp examples we arbitrarily decide that the array and function dual library choices should always match as to whether the Boost or C++ standard library be chosen. In order to enforce this match we could code up a header file for the sole purpose of producing a preprocessor error if our array and function dual library choices do not match, and then include that header file in all are header-only library header files as a check to make sure they match whenever any of our header files is included. Our checking header file might be called MyHeaderOnlyLibCheck.hpp:

// Header file MyHeaderOnlyLibCheck.hpp
#include <boost/cxx_dual/impl/array.hpp>
#include <boost/cxx_dual/impl/function.hpp>
#include <boost/cxx_dual/valid_variants.hpp>
#if !CXXD_VALID_VARIANTS((CXXD_ARRAY,0)(CXXD_FUNCTION,0),(CXXD_ARRAY,1)(CXXD_FUNCTION,1))
    #error MyLib - CXXD configuration not supported where array and function differ.
#endif

We now include that header file in all of the header files of our header-only library and every time the end-user uses one of our header files a check is made to ensure that the dual libraries for array and function match.

Of course we could simply eschew the use of the CXXD_VALID_VARIANTS macro in this case and simply code up our MyHeaderOnlyLibCheck.hpp header file in this way:

// Header file MyHeaderOnlyLibCheck.hpp
#include <boost/cxx_dual/impl/array.hpp>
#include <boost/cxx_dual/impl/function.hpp>
#if !((CXXD_HAS_STD_ARRAY && CXXD_HAS_STD_FUNCTION) || (!CXXD_HAS_STD_ARRAY && !CXXD_HAS_STD_FUNCTION))
    #error MyLib - CXXD configuration not supported where array and function differ.
#endif

Whether the possibility of using the CXXD_VALID_VARIANTS macro is considered for a non-header only library, its main purpose, or a header-only library, the choice of using the CXXD_VALID_VARIANTS macro or manually test the individual CXXD_HAS_STD_'XXX' macros is totally up to the library author.


PrevUpHomeNext