Quick Links: |
Provide machinery to deprecate interfaces on a per-version basis. More...
BSLS_DEPRECATE | tag an interface as deprecated |
BSLS_DEPRECATE_IS_ACTIVE | conditionally activate deprecation by UOR version |
BSLS_DEPRECATE_MAKE_VER | render UOR version for deprecation threshold |
typedef
s, and the conditional compilation of enumerators and preprocessor macros. The bsls_deprecate
facility operates by triggering compiler warnings when types or interfaces are used that have been tagged with deprecation macros defined by this component. Unlike previous deprecation facilities based exclusively on the use of #ifndef
with global macros (such as BDE_OMIT_DEPRECATED
and BDE_OMIT_INTERNAL_DEPRECATED
), supported use of the bsls_deprecate
facility does not affect a UOR's ABI. It is therefore safe to link applications based on libraries built with different deprecation policies. BSLS_DEPRECATE
macro, wrapped in a #if
block to apply BSLS_DEPRECATE
only when BSLS_DEPRECATE_IS_ACTIVE(UOR, M, N)
evaluates to true for a given version M.N
of the specified UOR
: #if BSLS_DEPRECATE_IS_ACTIVE(BDE, 3, 2) BSLS_DEPRECATE #endif int foo(const char *bar); // !DEPRECATED!: Use 'newFoo' instead.
BSLS_DEPRECATE_IS_ACTIVE
indicates that foo
is deprecated starting with bde
version 3.2. Once the deprecation threshold for bde
advances to version 3.2, code calling foo
will generate a deprecation warning with compilers that support deprecation attributes. (See Version Control Macros for Library Authors for information on defining a deprecation threshold for a UOR.) Note that in the absence of an explicit deprecation threshold (BDE_VERSION_DEPRECATION_THRESHOLD
, in this case), code calling foo
would begin generating deprecation warnings in the very next minor or major release of bde
(3.3 or 4.0, whichever applies). BSLS_DEPRECATE_IS_ACTIVE
: // bdexyz_component.h -*-C++-*- #if BSLS_DEPRECATE_IS_ACTIVE(BDE, 3, 2) #define BDEXYZ_COMPONENT_DEPRECATED_3_2 BSLS_DEPRECATE #else #define BDEXYZ_COMPONENT_DEPRECATED_3_2 #endif // ... BDEXYZ_COMPONENT_DEPRECATED_3_2 int foo(); BDEXYZ_COMPONENT_DEPRECATED_3_2 int bar(); // ... #undef BDEXYZ_COMPONENT_DEPRECATED_3_2
BSLS_DEPRECATE
as shown above, the deprecation is initially not enforced by default. That is, a normal build of code calling the deprecated interface will not emit a deprecation warning. BB_WARN_ALL_DEPRECATIONS_FOR_TESTING_ONLY
in their build system. $ CXXFLAGS=-DBB_WARN_ALL_DEPRECATIONS_FOR_TESTING_ONLY make my_application # A compiler that supports 'BSLS_DEPRECATE' will emit a warning if any # deprecated interfaces are used in 'my_application', even if those # deprecations are scheduled to take effect in a future release.
BB_WARN_ALL_DEPRECATIONS_FOR_TESTING_ONLY
in a PRODUCTION BUILD CONFIGURATION. If you do so, all libraries that you depend on will be prevented from deprecating more code in future versions. BSLS_DEPRECATE
, the library owner can make new uses of that interface generate warnings by defining a deprecation threshold for the UOR that contains the deprecated interface (or by adjusting the deprecation threshold for the UOR if it already exists). Defining a deprecation threshold enforces deprecations made in all versions up to and including the threshold. If the version number of the deprecation threshold is greater than or equal to the version number specified in the BSLS_DEPRECATE_IS_ACTIVE(UOR, M, N)
macro, then the BSLS_DEPRECATE
macro will be enabled and generate a warning. // bdescm_versiontag.h // ... #define BDE_VERSION_DEPRECATION_THRESHOLD BSLS_DEPRECATE_MAKE_VER(3, 2) // All 'bde' deprecations tied to release 3.2 or earlier will trigger // compiler warnings.
#ifdef
to remove deprecated code when it is built with appropriate options. These solutions have a practical shortcoming in a production environment like Bloomberg's, where code changes in lower-level libraries cannot be checked in if they break the build of (higher-level) client libraries. In such a system, well-meaning clients might build their libraries using -Werror
(to turn compilation warnings into errors) or with appropriate #ifdef
s to ensure their code does not use deprecated APIs, but in so doing they hinder the introduction of any new deprecations. In addition, the use of #ifdef
results in ABI compatibility issues, as some clients may build with deprecated code removed, and others may not. BSLS_DEPRECATE_*
macros are used to trigger compilation warnings on platforms that support deprecation attributes, instead of removing code from the codebase. BSLS_DEPRECATE
macro is generally guarded by a version check using BSLS_DEPRECATE_IS_ACTIVE
. In an environment where compiler warnings are considered to be build failures, it is possible (and encouraged) to tag a C++ entity with a deprecation macro in one release cycle, and not have that deprecation affect any clients by default until a later release cycle. During the intervening period, clients have an opportunity to proactively check their code for uses of newly-deprecated code. BSLS_DEPRECATE
: BSLS_DEPRECATE
expands to nothing. BSLS_DEPRECATE
can be applied to class
or struct
definitions, function declarations, and typedef
s.BSLS_DEPRECATE_IS_ACTIVE(UOR, M, N)
: M.N
of the specified UOR
, and to 0 otherwise.#if
block in front of a function, type, or typedef
declaration, with BSLS_DEPRECATE_IS_ACTIVE(UOR, M, N)
controlling whether or not BSLS_DEPRECATE
is applied to the declaration. The exact placement of the block should match the requirements of the C++14 [[deprecated]]
attribute. class #if BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 2) BSLS_DEPRECATE #endif SomeType { // ... }; struct SomeUtil { #if BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 2) BSLS_DEPRECATE #endif static int someFunction(); }; #if BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 2) BSLS_DEPRECATE #endif typedef SupportedType DeprecatedType;
BSLS_DEPRECATE
cannot be applied uniformly to some C++ constructs, most notably variables, enumerators, and preprocessor macros. Fortunately, these constructs can often be removed from a library without otherwise affecting ABI, so !BSLS_DEPRECATE_IS_ACTIVE(UOR, M, N)
can be used with a #if
directive to entirely remove blocks of code containing those C++ constructs. #if !BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 2) #define MY_DEPRECATED_MACRO // Macro definitions can be removed with 'BSLS_DEPRECATE_IS_ACTIVE'. #endif // !BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 2) namespace grppkg { #if !BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 3) SomeType myDeprecatedGlobalVariable; // Variables at 'namespace' or global scope can be removed with // 'BSLS_DEPRECATE_IS_ACTIVE'. #endif // !BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 3) // ... } // close namespace 'grppkg'
!
operator: deprecated code is compiled only if deprecations are not enforced for the specified UOR version. enum
: enum MyEnum { e_FIRST, e_SECOND, e_THIRD, #if !BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 2) // These legacy enumerators can be deprecated because their removal // does not affect the values of any other enumerators. FIRST = e_FIRST, SECOND = e_SECOND, THIRD = e_THIRD #endif };
<UOR>_VERSION_DEPRECATION_THRESHOLD
: <uor>scm_versiontag.h
alongside <UOR>_VERSION_MAJOR
and <UOR>_VERSION_MINOR
to indicate the greatest version of the unit of release UOR
for which deprecations are enforced by default.// abcscm_versiontag.h #define ABC_VERSION_MAJOR 1 #define ABC_VERSION_MINOR 4 #define ABC_VERSION_DEPRECATION_THRESHOLD BSLS_DEPRECATE_MAKE_VER(1, 2)
BSLS_DEPRECATE_IS_ACTIVE(ABC, M, N)
will expand to 1 for all versions M.N
of ABC
up to and including version 1.2. For M.N
later than 1.2 (e.g., 1.3 or 2.0), BSLS_DEPRECATE_IS_ACTIVE(ABC, M, N)
will expand to 1 only if the BB_WARN_ALL_DEPRECATIONS_FOR_TESTING_ONLY
macro is defined by the user (see Build Control Macros for Clients). <UOR>_VERSION_MAJOR
and <UOR>_VERSION_MINOR
, then BSLS_DEPRECATE_IS_ACTIVE(UOR, M, N)
will expand to 1 once the version indicated by <UOR>_VERSION_MAJOR
and <UOR>_VERSION_MINOR
becomes greater than M.N
. For example, BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 4)
will expand to 1 in version 1.5 of ABC
(or 2.0 if there is no release 1.5) if ABC_VERSION_DEPRECATION_THRESHOLD
is not defined. For this reason, it is highly recommended that UOR authors explicitly define a deprecation threshold to avoid unexpected build failures when a new release is issued, especially in environments where warnings are considered fatal. BB_SILENCE_DEPRECATIONS_FOR_BUILDING_UOR_<UOR>
: bsls_deprecate
from enforcing deprecations for all versions of UOR
. This macro must be defined in each .cpp
file of UOR
that either uses a deprecated interface from the same UOR that has reached the deprecation threshold for UOR
, or includes a header file of UOR
that uses such an interface in inline code. This macro must be defined before the first #include
of a header from UOR
.// abcxyz_somecomponent.cpp #define BB_SILENCE_DEPRECATIONS_FOR_BUILDING_UOR_ABC #include <abcxyz_somecomponent.h> // ...
BB_WARN_ALL_DEPRECATIONS_FOR_TESTING_ONLY
: -D
parameter during test builds of components that are intended to be deprecation-clean. When this macro is defined, deprecations will be enforced for all versions of all UORs, except as overridden by BB_SILENCE_DEPRECATIONS_FOR_BUILDING_UOR_<UOR>
(see Version Control Macros for Library Authors or BB_SILENCE_DEPRECATIONS_<UOR>_<M>_<N>
(see below). This macro must never appear in source code, and must never be defined for any production or check-in build configuration.BB_SILENCE_DEPRECATIONS_<UOR>_<M>_<N>
: UOR
who still need to use an interface that was deprecated in version M.N
after the deprecation threshold for UOR
has reached (or exceeded) M.N
. This macro should be defined before the first #include
of a header from UOR
. This macro must never be defined in a header file.// grppkg_fooutil.cpp #define BB_SILENCE_DEPRECATIONS_ABC_1_2 // 'BB_SILENCE_DEPRECATIONS_ABC_1_2' must be defined before the // component's own '#include' directive in case 'grppkg_fooutil.h' // includes headers from 'abc' (directly or transitively). #include <grppkg_fooutil.h> // Interfaces from 'abcxyz_someutil' deprecated in version 1.2 of 'abc' // will not trigger compiler warnings when used in 'grppkg_fooutil.cpp'. #include <abcxyz_someutil.h> ... namespace grppkg { void FooUtil::foo() { int result = abcxyz::SomeUtil::someFunction(); // ... } } // close package namespace
BSLS_DEPRECATE
will produce a warning with the following compilers: BSLS_DEPRECATE
will produce a warning with any compiler that provides the C++14 deprecated
attribute. abc
is currently at version 1.1. If the owners of abc
want to deprecate a function abcxyz::SomeUtil::someFunction
, they could add the deprecation macros BSLS_DEPRECATE
and BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 2)
in front of the declaration of someFunction
: // abcxyz_someutil.h ... struct SomeUtil { ... #if BSLS_DEPRECATE_IS_ACTIVE(ABC, 1, 2) BSLS_DEPRECATE #endif static int someFunction(); // DEPRECATED: use 'abcdef::OtherUtil::otherFunction' instead. ... };
abc
, so the deprecation macro alone will not have any affect on clients. A client building their code normally will trigger no compiler warnings due to this new deprecation: $ make grppkg_fooutil ... dependencies: abc version 1.2 ... ... no warnings ...
BB_WARN_ALL_DEPRECATIONS_FOR_TESTING_ONLY
flag in their test build process. A compiler that supports deprecation attributes will then trigger compiler warnings: $ CXXFLAGS=-DBB_WARN_ALL_DEPRECATIONS_FOR_TESTING_ONLY make grppkg_fooutil
... dependencies: abc version 1.2 ...
grppkg_fooutil.cpp:43: warning: function 'abcxyz::SomeUtil::someFunction'
is explicitly deprecated.
BB_WARN_ALL_DEPRECATIONS_FOR_TESTING_ONLY
in a production build. This flag should be used for development builds only. abcxyz::SomeUtil::someFunction
. abc
version 1.3, the owners of abc
will make deprecations enforced by default for version 1.2 of abc
, by setting the deprecation threshold for abc
in their version control headers: // abcscm_versiontag.h #define ABC_VERSION_MAJOR 1 #define ABC_VERSION_MINOR 3 ... #define ABC_VERSION_DEPRECATION_THRESHOLD BSLS_DEPRECATE_MAKE_VER(1, 2)
grp
have cleaned up their code, normal builds will trigger no compiler warnings. However, any new development by any clients will trigger warnings if they add a new use of abcxyz::SomeUtil::someFunction
: $ make foobar_bazutil
... dependencies: abc version 1.3 ...
foobar_bazutil.cpp:177: warning: function 'abcxyz::SomeUtil::someFunction'
is explicitly deprecated.
grp
will not, or for some reason cannot, clean up their code in the near term? Moving the threshold for abc
to version 1.2 will of course trigger warnings when the owners of grp
next try to build grppkg_fooutil
. In a development context requiring that all production builds remain warning-free, we would be at an impasse: either the owners of grp
must clean up their code immediately, or the owners of abc
will not be able to enforce deprecations by default for version 1.2. grp
to locally silence warnings caused by deprecations for version 1.2 of abc
. This is done by adding a definition of BB_SILENCE_DEPRECATIONS_ABC_1_2
to any .cpp
files that use abcxyz::SomeUtil::someFunction
: // grppkg_fooutil.cpp #define BB_SILENCE_DEPRECATIONS_ABC_1_2 ...
BB_SILENCE_DEPRECATIONS_<UOR>_<M>_<N>
macros in the codebase, and put pressure on teams to remove such remnant uses of deprecated code. When all remnant uses have been removed, then the deprecated function can be deleted entirely: // abcxyz_someutil.h ... struct SomeUtil { ... // RIP: The function formerly known as 'someFunction'. ... };
BSLS_DEPRECATE
and BSLS_DEPRECATE_IS_ACTIVE
, and their associated control macros, can be used to gradually reduce the number of uses of deprecated code, so that it can be removed eventually. xxx
that is currently at version 7.6. One of our components contains a function foo
that has been superseded by another function bar
. int foo(int *coefficient, int n); // Load into the specified 'coefficient' the (positive) Winkelbaum // Coefficient of the specified 'n'. Return 0 on success, and a // negative number if there is no coefficient corresponding to 'n'. // Note that every integer divisible by the Winkelbaum Modulus (17) has // a corresponding Winkelbaum Coefficient. // ... int bar(int n); // Return the (positive) Winkelbaum Coefficient of the specified 'n'. // The behavior is undefined unless 'n' is divisible by 17 (the // Winkelbaum Modulus).
foo
, showing that it will be deprecated starting with version 7.7, and update the documentation accordingly: #if BSLS_DEPRECATE_IS_ACTIVE(XXX, 7, 7) BSLS_DEPRECATE #endif int foo(int *coefficient, int n); // !DEPRECATED!: Use 'bar' instead. // // Load into the specified 'coefficient' the (positive) Winkelbaum // Coefficient of the specified 'n'. Return 0 on success, and a // negative number if there is no coefficient corresponding to 'n'. // Note that every integer divisible by the Winkelbaum Modulus (17) has // a corresponding Winkelbaum Coefficient. // ... int bar(int n); // Return the (positive) Winkelbaum Coefficient of the specified 'n'. // The behavior is undefined unless 'n' is divisible by 17 (the // Winkelbaum Modulus).
foo
. However if any of those users do a test build of their code with -DBB_WARN_ALL_DEPRECATIONS_FOR_TESTING_ONLY
, they will see a warning that foo
has been deprecated. foo
to switch over to using bar
, probably on or after the release of xxx
version 7.8, we can enforce the deprecation of foo
by moving the deprecation threshold for xxx
to version 7.7, to indicate that all interfaces deprecated for version 7.7 are disallowed by default: // xxxscm_versiontag.h #define XXX_VERSION_MAJOR 7 #define XXX_VERSION_MINOR 8 // ... #define XXX_VERSION_DEPRECATION_THRESHOLD BSLS_DEPRECATE_MAKE_VER(7, 7)