Version 40 (modified by 13 years ago) ( diff ) | ,
---|
- Motivation
- Introduction
-
Developer Guidelines
- Announce new library features
- Make feature request for each feature
-
Document
- Tag Boost library with specific library version
- Include the tracked tickets on the Release notes
- List the test cases associated to the trac system tickets
- List the dependency upon other Boost library
- Document behavior differences between release and debug variants
- Document behavior differences between toolsets
-
Coding
- Be careful with the use of using namespace header files
- Don't overload functions that are used by the TR1
- Avoid include-all-features files at the /boost level
- Don't refine functions overloading without ensuring the same behavior
- Avoid the inclusion of symbols at the boost or boost::detail namespace
- Avoid different external behavior depending on the variant release or …
- Avoid to change interfaces
- Don't delete files prematurely
- Don't delete namespaces prematurely
- Don't delete classes/functions/variables prematurely
- Don't modify functions prototypes prematurely
- Remove the deprecated features on a given release
- Test
- User guidelines
- Boosters guidelines
MAINTENANCE GUIDELINES
WARNING: The contents of this page are not the result of a consensus of the Boost community.
Please feel free to improve this page directly or post on the Boost mailing lists boost-AT-lists.boost.org and boost-users-AT-lists.boost.org.
Motivation
To be added
Introduction
Nobody wants to completely disallow changes. We need them.
Everybody wants to avoid breaking changes, but sometimes these are also necessary.
This page describes guidelines that could help the Boost community to get stable libraries and as a consequence to avoid user code breaking when the user upgrades to a more recent Boost release.
The difference between breaking changes and bugs concerns documentation. A documented modification breaking user code cannot be considered a bug while the same undocumented modification could be a bug.
User code breaking cases
Why can user code break when the user upgrades to a more recent Boost release? Here are some examples which demonstrate the most common problems, although they do not pretend to be exhaustive.
- Syntactic breaking: detected at compile time
It is evident that the removal of files, namespaces, classes, function, variables or macros could break user code. What it is less evident is that the addition of namespaces, classes, functions, variables at the namespace level or macros can also break user code. Note that modifications can be considered as deletion+addition.
The following code example
#include <boost/file.hpp> using namespace boost; class foo { // ... }; void bar() { // ... }
breaks when
- we add the namespace foo in file.hpp
// boost/file.hpp namespace boost { namespace foo { // ... } }
- we add the class foo in file.hpp
// boost/file.hpp namespace boost { class foo { // ... }; }
- we add the function bar in file.hpp
// boost/file.hpp namespace boost { void bar() { // ... } }
Adding macros Use code should not break if we preserve the macro naming rule BOOST_LIBNAME_NAME.
- Semantic breaking: detected at runtime
Sometimes user code will compile with the upgraded Boost release, but the code will not behave as before. Changes that can lead to this situation must be documented particularly prominently because the compiler can not help the user to catch the broken code.
The following code example
#include <boost/file.hpp> using namespace boost; void bar(int i) { // ... }
breaks when
- we add the function bar in one of the files included by the user program
// boost/file.hpp namespace boost { void bar(char c) { // ... } }
These guidelines are not only addressed to Boost library developers, but also to users and the release manager team (RM). Every member of the Boost community has his role to play. Note that the author also plays the role of a user of all the libraries on which his library depends and of his own library when writing the library tests.
The key issues to get stable libraries are:
- Versioning individual Boost libraries.
- Using a deprecation period to give users notice that a breaking change is coming.
- Documenting and tracking the changes.
- Avoiding completely silent breakage by a deep inspection of code changes and running the regression tests (functional tests) from the _previous_ major release against the current release as a way to automatically detect and flag interface/behavioral changes.
These guidelines are related mainly to how to document changes, how breaking changes can be detected either by test or by inspections, and of course guidelines related to the code itself.
Versioning individual Boost libraries
This page don't contains any advice related to individual Boost libraries packaging. There is already the CMake project working on that. But tagging individual Boost libraries with a specific version MAJOR.MINOR.PATCH has already the informational advantage.
Versions are denoted using a standard triplet of integers: MAJOR.MINOR.PATCH. The basic intent is that MAJOR versions are incompatible, large-scale upgrades of the API. MINOR versions retain source and binary compatibility with older minor versions, and changes in the PATCH level are perfectly compatible, forwards and backwards.
Deprecating features
C++ don't have deprecated attributes, so the developer needs to emulate a warning when some deprecated feature is used. The #warning directive can be used to this purpose.
#warning "name is deprecated, please use new_name instead"
The main problem is that this warning will appear independently of whether the user code uses or not the deprecated feature. In order to palliate to this the inclusion of the deprecated feature could be accessible conditionally and only when included the warning will be reported or not.
For compatibility purposes deprecated features should be included by default. So the library behaves as if the features were not been deprecated. If a user don't want to include a deprecated feature he/she can define one of the following macros:
- BOOST_DONT_INCLUDE_DEPRECATED: don't include any deprecated feature.
- BOOST_DONT_INCLUDE_DEPRECATED_ON_X_Y: don't include any deprecated feature to be removed on version X.Y.
- BOOST_LIBX_DONT_INCLUDE_DEPRECATED: don't include any deprecated feature for library Boost.LIBX.
- BOOST_LIBX_DONT_INCLUDE_DEPRECATED_ON_X_Y: don't include any deprecated feature for libarary Boost.LIBX to be removed on version X.Y.
- BOOST_LIBX_DONT_INCLUDE_DEPRECATED_NAME: don't include the deprecated feature name.
Once one of this macros is defined the developer can define another macro to make easier the work.
- BOOST_LIBX_NAME_DECLARED: states that the deprecated feature name has been declared
Deprecated warnings are not reported by default, so the library behaves as if the features were not been deprecated. If the user wants this deprecated warnings to appear he/she can define one of the macros:
- BOOST_WARNS_DEPRECATED: warms on all deprecated features
- BOOST_WARNS_DEPRECATED_ON_X_Y: warms on all deprecated features to be removed on version X.Y
Cross version testing
Running the regression tests from the _previous_ major release against the current release is a way to automatically detect and flag interface/behavioral changes. Sure, there are many potential problems with such an approach:
- Some library tests are likely to also exercise code at the implementation/detail level.
- Already limited testing resources (on the other hand, interface changes are likely to be detected by running such tests for a single, reasonably compliant, compiler).
- ...
Another approach consist in preserving the test of the older releases and run them on the current release. If the developer needs to change the tests while she/he evolves his library this is a symptom the interface has been changed and the same way the tests are broken, the user code can also be broken. If the developer forbids himself to modify the test, he will be able to identify breaking changes early on.
Developer Guidelines
Announce new library features
It is a good idea the developer announce to the Boost mailing lists the new features just before committing in the release branch. This will let the time to the Boost community to inspect the modifications introduced.
Make feature request for each feature
Each modification deprecated/suppressed/modified/new feature should be tracked with a ticket on the Trac system.
Document
Tag Boost library with specific library version
Tagging each library release with a version, is the more visible way to signal possible incompatibilities to the user.
For example the thread library could have:
- 2.1.0 Changes since boost 1.35
- 2.0.0 Changes since boost 1.34
- 1.0.0 Initial release on 1.25
Include the tracked tickets on the Release notes
The release notes should include
- the list of all the bug tickets that have been solved
- the list of all deprecated/suppressed/modified/new features with their associated tickets.
URL: https://svn.boost.org/svn/boost/website/public_html/beta/feed/history
There will be a file in that directory named boost_n_nn_n.qbk, where n_nn_n is the release number. Edit the [section Updated Libraries] portion of that file to add a description of changes to your library. For example:
* [phrase library..[@/libs/interprocess/index.html Interprocess]:] * Updated documentation to show rvalue-references functions instead of emulation functions. * More non-copyable classes are now movable. * Move-constructor and assignments now leave moved object in default-constructed state instead of just swapping contents. * Several bug fixes: *[@https://svn.boost.org/trac/boost/ticket/2391 #2391] interprocess_recursive_mutex doesn't work interprocess, *[@https://svn.boost.org/trac/boost/ticket/2431 #2431] A Few More Corrections for the Interprocess Documentation, *[@https://svn.boost.org/trac/boost/ticket/1390 #1390] managed shared memory failing completely under OSX, *[@https://svn.boost.org/trac/boost/ticket/2570 #2570] boost::interprocess::message_queue::timed_send and timed_receive bug, *[@https://svn.boost.org/trac/boost/ticket/2528 #2528] Missing include of iostream in interprocess tests.
or
* [phrase library..[@/libs/functional/hash/index.html Hash]:] * `boost/functional/detail/container_fwd.hpp` has been moved to `boost/detail/container_fwd.hpp`. The current location is deprecated. * For more detail, see the [@/doc/html/hash/changes.html#hash.changes.boost_1_38_0 library changelog].
List the test cases associated to the trac system tickets
Each feature request or bug should have associated at least a test case. A table showing this association will help to check that each feature,bug is has the expected test coverage.
Ticket | Synopsis | Test Case |
#XXXX | foo foo foo bar bar bar | test_foo_bar |
List the dependency upon other Boost library
Each library lists the other libraries it depends upon and the minimum version # - as it does with compilers now
Library | Version # | Build & Link | Comment |
Thread | 2.1.0 | link | generic lock() |
Document behavior differences between release and debug variants
Document the differences in behavior that depends on the user defines, in particular the release and debug variants.
Document behavior differences between toolsets
Document the differences in behavior that depends on the toolsets.
Coding
Be careful with the use of using namespace header files
To import symbols from another namespace the developer can use the using directive using namespace but limited to the function level.
Don't overload functions that are used by the TR1
Overloading imported Boost.TR1 functions is equivalent to overload the std functions.
Avoid include-all-features files at the /boost level
Avoid include-all-file at the /boost level including all the functionalities of the library. Provide instead specific files at boost/lib/file.hpp.
If you provide such files ensure that your tests works when you include them directly or include the specific ones.
Don't refine functions overloading without ensuring the same behavior
If you need document them and warm the user.
Avoid the inclusion of symbols at the boost or boost::detail namespace
These symbols can collide with symbols of other libraries.
Announce them on the Boost mailing list when you think this is the best choice.
Avoid different external behavior depending on the variant release or debug without documenting it
Providing different external behavior depending on the variant release or debug could be a surprise for the user.
If you need them document it and warm the user.
Avoid to change interfaces
When you change the interface, you can see the changes that are needed at the Boost level, but you can not evaluate the cost induced by this change for the users. So before to change an interface deprecate it and let the user enough time to migrate to the new one.
Don't delete files prematurely
Before deleting a file, "file.hpp", deprecate it and let the user modify his code during some versions (e.g. until 1_40). Replace it by
// boost/old_header_file.hpp // include whatever file is needed to preserve the old file contents #if defined(BOOST_WARNS_DEPRECATED) || defined(BOOST_WARNS_DEPRECATED_ON_1_40) #warning "boost/old_header_file.hpp is deprecated, please include boost/new_header_file.hpp instead #endif
It will be up to the user to define the macros BOOST_WARNS_DEPRECATED and BOOST_WARNS_DEPRECATED_ON_1_40 when the user wants to be warmed for deprecated features on Boost or on the Boost version 1.40 respectively.
Don't delete namespaces prematurely
Use the using sentence to import from the new namespace to the old one.
Don't delete classes/functions/variables prematurely
Instead of deleting a class or a public/protected function/variable on the next version protect its declaration by any of the DONT_INCLUDE_DEPRECATED macros.
// boost/libx/header_file.hpp namespace boost { namespace libx { #if defined(BOOST_DONT_INCLUDE_DEPRECATED) || defined(BOOST_DONT_INCLUDE_DEPRECATED_ON_1_41) \ || defined(BOOST_LIBX_DONT_INCLUDE_DEPRECATED) || defined(BOOST_LIBX_DONT_INCLUDE_DEPRECATED_ON_1_41) \ || defined(BOOST_LIBX_DONT_INCLUDE_DEPRECATED_NAME) #else #define BOOST_LIBX_NAME_DECLARED #if defined(BOOST_WARNS_DEPRECATED) || defined(BOOST_WARNS_DEPRECATED_ON_1_40) #warning "name is deprecated, please use new_name instead" #endif class name { name::name(); // as before }; #endif } }
When the declaration should been included, the developer could define a BOOST_LIBX_NAME_DECLARED, which could be used on the class/function/variable definition. E.g.
// boost/libx/header_file.cpp namespace boost { namespace libx { #if defined(BOOST_LIBX_NAME_DECLARED) name::name() { // as before } // as before #endif } }
Don't modify functions prototypes prematurely
Modifying an existing function prototype, if you were deleting it and adding the new one. Instead add the new when possible.
As both prototypes should coexist for some releases, check if this overloading could cause user code breaks.
Remove the deprecated features on a given release
Once the deprecated period is expired the developer sould remove them.
Don't forget to change the major version number.
Test
Test headers
Steve Watanabe has written a Jamfile that make easier this task. See /boost/libs/units/test/test_header for an example.
Include each header files twice The inclusion of a header file twice ensure that the file is self contained, and that it is well protected
#include <file.hpp> #include <file.hpp> int main() {return 0;}
Include all header files in both orders One more test could be useful, include all the Boost Headers files on both orders
#include <file1.hpp> #include <file2.hpp> ... #include <filen.hpp>
#include <filen.hpp> ... #include <file2.hpp> #include <file1.hpp>
Link all the header files twice This ought to catch non-inlined functions and other duplicate definitions
Don't forget to test
The implicitly generated member functions
The implicitly generated member functions are part of your interface.
Add some simple tests that prove its correct behavior.
The removed default member functions when you declare a constructor
When you declare a constructor the other default constructors are not generated, so
The deleted (private) default member functions
The default member functions =deleted (or declared private) are not part of the interface.
Add some simple tests that prove the compilation fails when you use them.
The explicit constructors
The implicit constructors or conversions
The const-ness of variables, function parameters and function return types and functions
When a variable, function parameter or function return type is non-const, test that you can modify them.
When a variable, function parameter or function return type is const, test that you try to modify them the compilation fails.
Separate the functional test from the unit test (implementation test)
Functional test should take in account only the documented behavior. They can be used as regression test for new re-factorings of the implementation.
Implementation test can be used to test details in the implementation but not as regression tests.
Preserve the functional test from the preceding versions
While doing modifications to the library the developer can find some regressions on some tests of the preceding versions. It seems natural to change them to make the test succeed. This is not a good idea. The failing test is a symptom that the new modifications could break user code.
If the modification don't pretended to be an interface break, the developer should modify the library until the preceding test cases pass.
If the break is intentional move the tests to the failing tests compile_fail or run_fail on the Jamfile. The developer can copy the test and adapt it to the new interface. It could be a good idea to state explicitly on the test file name on which version they were included.
In order to avoid erroneous modification of the the released tests files the developer could change the rights of the test files. If the developer has no permission to do that on the SVN repository as a last resort he could change the permission in its own SVN copy.
User guidelines
Don't use using directives
As seen before, using directives in user code is one of the sources of user code breaking, so it will be safer to use namespace synonyms instead and prefix each symbol with them.
Instead of doing
using namespace boost::interprocess; shared_memory_object::remove("MySharedMemory");
do
namespace bip = boost::interprocess; bip::shared_memory_object::remove("MySharedMemory");
Avoid the use of include all features files at the /boost level
Avoid the use of include all features files at the /boost level use instead specific files at boost/lib/file.hpp. Including less code reduce your user code breaking chances.
Help tracking regression test on the Trac system tickets
A bug ticket should be associated to one or several test cases, either the test cases exists already and we are in face of a regression for some toolset, or a new toolset is considered, or a new test case is needed to reproduce the bug and the modification solve the problem. State clearly which test cases reproduce the bug it they exists and propose a new test case reproducing the bug otherwise.
Boosters guidelines
Inspect the code
Inspect the code to check that the new modifications don't include user code breaks. Tools comparing the contents of the current release and the preceding one could help. Discuss the issue on the ML and do a ticket on the Trac system if there is something to improve.
Check that every modification has been documented
Inspect that every modification has been reported on the documentation. Tools comparing the contents of the current release and the preceding one could help. Discuss the issue on the ML and do a ticket on the Trac system if there is something to improve.
Check that every modification has been tested
Inspect that every modification has been tested. Tools comparing the contents of the current release and the preceding one could help. Discuss the issue on the ML and do a ticket on the Trac system if there is something to improve.
Managing Warnings from Compilers (and other tools)
Avoid warnings, Eliminate warning, Suppress Warnings, or Document.
Boost aims to avoid warnings messages as far as is reasonably practicable, even when compiled with the highest and most pedantic warning level, avoiding vendor specific extensions if possible.
Warnings often indicate real problems. Sometimes they only manifest on a particular platform, revealing a portability issue. Sometimes they indicate that the code doesn't account for a runtime condition, like overflow, which the warning can only suggest as a possibility.
Suppressing a warning without altering code may simply mask a problem. The right approach is to determine why the warning occurs is to decide whether it is correct in the context, and if so, apply appropriate remediation. If the warning is not correct in the context, only then should it be suppressed.
Because developers don't have the same knowledge, even among Boost developers, Boost is amassing information to help them know when a warning is significant and not. That information can show cases in which a warning is legitimate and when it isn't. For the former, there is help to understand how to change the code portably to account for the problem revealed by the warning. For the latter, there is information on how to suppress the warning in a portable way.
Changing code can lead to bugs. Thus, changing code to eliminate a warning might create a bug. That's unfortunate. From a maintenance standpoint, however, most would prefer to see altered code to a glob of preprocessor and pragma line noise that suppresses a warning in the unchanged code.
Testing will reveal the bug. If it doesn't, the testing is insufficient. If the bug appears on an untested platform, then more testers are needed to be able to detect such bugs in the future.
Breifly, for some users, any warning is a bug.
See also https://svn.boost.org/trac/boost/wiki/WarningFixes for progress made on warning fixes/suppression.
Reasons to eliminate or suppress warnings:
1 To allow users, whose environment requires no warnings, to use Boost code.
2 To avoid the nuisance, perhaps overwhelming, of spurious warning messages.
3 To improve code quality by focusing library writers' attention on potential problems.
4 To improve portability by focusing developer attention on potentially non-portable code.
5 To improve compliance with the C++ Standard.
6 To permit users to set high warning levels when using Boost libraries without being overwhelmed with a barrage of library warnings.
7 To document that warnings have been considered by the library author or maintainer and are considered not significant.
8 For Boost, making the suppression local is more important than only suppressing a specific warning.
What to do
1 Test compilation with the most pedantic setting for the compiler, and non-debug mode.
For Microsoft Visual Studio, this means setting level to 4 (command line /W4).
For code that doesn't deliberately use Microsoft language extensions, disable them with Disable MS extensions = Yes, command line option /Za
To a jamfile add <toolset>msvc:<cxxflags>/Za # disable MS extensions.
If it proves impossible to compile with the /Za option (it is reported to cause trouble with the MS compiler, sometimes), just document this, including in the build jamfile.
If only one (or a few) modules in a test (or other) build require MS extensions, you can selectively 'switch off' this 'disable' in the 'requirements' in the jamfile, for example:
# pow test requires type_of that MS extensions. run pow_test.cpp ../../test/build//boost_test_exec_monitor : # command line : # input files : # requirements -<toolset>msvc:<cxxflags>/Za # Requires type_of which requires MS extensions, so cancel /Za. : test_pow ; Note the leading - switches off the compiler switch /Za, producing output thus: compile-c-c++ ..\..\..\bin.v2\libs\math\test\test_pow.test\msvc-9.0\debug\asynch-exceptions-on\threading-multi\pow_test.obj pow_test.cpp using native typeof
(You might also consider using Microsoft /Wall if available. You may find
http://www.geoffchappell.com/viewer.htm?doc=studies/msvc/cl/c1xx/warnings/index.htm&tx=2,27&ts=0,3820
a helpful guide to the byzantine complexity of warnings).
(If you wish to make all of your VS projects in all VS solutions compile with other options by default, you might consider (carefully) editing (with a text editor, very carefully, after keeping a copy of the original) the file C:\Program Files\Microsoft Visual Studio 9.0\VC\VCWizards\1033\common.js to change from the Microsoft defaults. This does not seem to be officially documented as far as I know. It may save you changing the project properties repeatedly.
function AddCommonConfig(oProj, strProjectName,bAddUnicode,bForEmptyProject) in the sections for debug and/or release CLTool.DisableLanguageExtensions = true; // add to make the default NO MS extensions. LinkTool.LinkIncremental = linkIncrementalNo; // add CLTool.WarningLevel = WarningLevel_4; // change from 3 to 4 to making the default warning level 4
For GCC this means -Wall -pendantic
but you might consider adding specific warnings that are to be suppressed, for example:
-pedantic -Wall -Wno-long-long -Wno-unused-value
but this is a global setting, so you need to document that these warnings must be suppressed by users of this module. This may be acceptable if they are building a library of entirely Boost modules.
Using bjam add warnings=all to the invocation command line.
Putting options in jam files allows setting to be made for one or more specified compiler(s).
Some sample options in jamfile.v2 are:
project : requirements <toolset>gcc:<cxxflags>-Wno-missing-braces <toolset>darwin:<cxxflags>-Wno-missing-braces <toolset>acc:<cxxflags>+W2068,2461,2236,4070,4069 # Comments on warning message help readers who do not have ACC documentation to hand. <toolset>intel:<cxxflags>-nologo <toolset>msvc:<warnings>all # == /W4 -<toolset>msvc:<define>_DEBUG # Undefine DEBUG. <toolset>msvc:<define>NDEBUG # Define NO debug, or release. <toolset>msvc:<asynch-exceptions>on # Needed for Boost.Test <toolset>msvc:<cxxflags>/Za # Disable MS extensions. #-<toolset>msvc:<cxxflags>/Za # (Re-)Enable MS extensions if these are definitely required for specific module. # The define of macros below prevent warnings about the checked versions of SCL and CRT libraries. # Most Boost code does not need these versions (as they are markedly slower). <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS <toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS <toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE <toolset>msvc:<define>_CRT_NONSTDC_NO_DEPRECATE # Suppresses other warnings about using standard POSIX and C9X. # Alternatively, you can just suppress the warnings (perhaps not the best way). <toolset>msvc:<cxxflags>/wd4996 # 'putenv': The POSIX name for this item is deprecated. <toolset>msvc:<cxxflags>/wd4512 # assignment operator could not be generated. <toolset>msvc:<cxxflags>/wd4224 # nonstandard extension used : formal parameter 'arg' was previously defined as a type. <toolset>msvc:<cxxflags>/wd4127 # expression is constant. <toolset>msvc:<cxxflags>/wd4701 # unreachable code - needed for lexical cast - temporary for Boost 1.40 & earlier. ... ;
Using warning-as-errors will make it hard to miss warnings.
Microsoft command line option /WX, gcc option -warning-as-errors gcc option -pedantic-errors
2 Consider each warning and
a Rewrite the code to avoid the warning, if possible. For example, adding a static_cast will indicate that any warning about loss of accuracy has been judged not possible or significant. Remove or comment out parameters to avoid warnings about unused parameters. Placing /* comment */ around an unused variable name, allows the name still to be useful documentation.
b Use the compiler specific mechanism to suppress the warning message, but try hard to ensure that this is as local to the package as possible so that users can still get warnings from *their code*.
For MSVC, this involves pushing to save the current warning state, disabling the warning, and later popping to restore (abbreviated as push'n'pop).
#if defined(_MSC_VER) #pragma warning(push) // Save warning settings. #pragma warning(disable : 4996) // Disable deprecated localtime/gmtime warning. #endif ... #if defined(_MSC_VER) #pragma warning(pop) // Restore warnings to previous state. #endif
Adding some or all of the warning message as a comment is helpful to tell readers what warning is being ignored, for example:
# pragma warning(disable: 4510) // default constructor could not be generated
If the warning is only for a specific compiler version, use this approach:
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) #pragma warning(push) #pragma warning(disable:4512) //assignment operator could not be generated #endif ... #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) #pragma warning(pop) #endif
c Repeat this process with other compilers and use their specific suppression methods.
d If a warning cannot be eliminated or suppressed, explain why in the code and the documentation. If appropriate, document in build files, as well. Consider indicating the highest warning level possible or compiler-specific settings that will provide a warning-free build.
Specific Warnings and Suggested Actions.
These are just few for starters, and need more, especially for gcc. Suggestions may be wrong!
The pragma or other code required to suppress warnings is in the right hand box, ready for copy and paste.
Microsoft
If you chose to suppress (rather than fix by recoding), localise the warnings as far as possible by using push'n'pop pragmas (see above).
Warning | Warning Description | Suggestions | Suppression |
C4100 | unreferenced formal parameter | Either surround the parameter with C comments, for example: int */ my_variable */)or just delete if the variable name is uninformative. If in other's module(s), or you are too busy/idle, at least suppress warning. In generic code you might not be able to comment the variable name as it might actually be used to call a static function. In this case simply referencing the variable (varname;) in the function body eliminates the warning. | # pragma warning(disable: 4100) 'name' : unreferenced formal parameter |
C4127 | conditional expression is constant | Very common and many believe unhelpful, but a few find it informative, so do not suppress globally. Even while(true) can trigger this! Suppress. while(true) can be rewritten as for(;;) eliminating the warning. | # pragma warning(disable: 4127) conditional expression is constant. |
C4146 | unary minus operator applied to unsigned type, result still unsigned | This indicates a real error. | Fix. |
C4180 | qualifier applied to function type has no meaning; ignored | The meaningless qualifier can always be removed (or left as a comment if it informative) - but check that you didn't mean to put the const somewhere else. can always be suppressed. | # pragma warning(disable: 4180) qualifier applied to function type has no meaning; ignored |
C4189 | local variable is initialized but not referenced | This probably indicates a redundant variable and assignment, so probably remove it. If you are sure it is required (or has negligible cost for some documentation benefit), suppress. | # pragma warning(disable: 4189) local variable is initialized but not referenced |
C4224 | nonstandard extension used : formal parameter 'arg' was previously defined as a type. | This will bite users who try to compile with Microsoft extensions disabled. So is most undesirable, but may be a major nuisance to change the names in code.However fixing is the Right Thing, but meanwhile suppressing may be helpful. | # pragma warning(disable: 4224) formal parameter 'arg' was previously defined as a type. |
C4244 | Conversion: possible loss of data. | Fix, for example changing type or using static_cast is best, suppress with much caution. | # pragma warning(disable:4244) Conversion: possible loss of data. |
C4324 | structure was padded due to declspec(align()) | Suppress | #pragma warning(disable:4324) structure was padded due to declspec(align()) |
C4503 | decorated name length exceeded | Suppress. (Note that \boost\config\compiler\visualc.hpp includes this global suppression: #pragma warning(disable:4503) | #pragma warning( disable : 4503 ) warning: decorated name length exceeded |
C4506 | no definition for inline function | Provide a definition, or if you cannot/will not, suppress. | # pragma warning(disable: 4506) no definition for inline function |
C4512 | assignment operator could not be generated | Suppress using push'n'pop for the module(s) causing the warning. Adding the declaration (not the definition) of the appropriate operator=() as a private member does the trick as well. | # pragma warning(disable: 4512) assignment operator could not be generated. |
c4511 | copy constructor could not be generated. | Provide constructor (and assignment operator and destructor). Or suppress. | # pragma warning(disable: 4511) copy constructor could not be generated |
C4510 | default constructor could not be generated. | If have reference or const members provide default constructor with initializer list. If intent is to make non-default-constructable, provide private declaration only. Suppress. | # pragma warning(disable: 4510) default constructor could not be generated |
C4535 | calling _set_se_translator() requires /EHa | Common from Boost.Test. In jamfile, add to project : requirements | <toolset>msvc:<asynch-exceptions>on # calling _set_se_translator() requires /EHa |
C4625 | copy constructor could not be generated because a base class copy constructor is inaccessible | Trying to derive from a non-copyable class? Bug? | Fix. |
C4626 | assignmentconstructor could not be generated because a base class copy constructor is inaccessible | Trying to derive from a non-copyable class? Bug? | Fix. |
C4671 | the copy constructor is inaccessible | Suppress? | #pragma warning(disable: 4671) the copy constructor is inaccessible |
C4673 | A throw object cannot be handled in the catch block. | Fix ? | #pragma warning(disable: 4673) Thrown object cannot be handled in catch block. |
C4701 | local variable may be used without having been initialized | Best is to recode to avoid the message, probably just initialising it. But if you are very sure the message is misleading, and cost of dummy initialisation too high, suppress. | # pragma warning(disable: 4701) local variable may be used without having been initialized |
C4702 | unreachable code | Best delete or comment-out. Be very cautious about suppressing this, but use of macros may make this troublesome, so suppress with care, and always locally. | #pragma warning(disable: 4702) unreachable code |
C4710 | 'function' : function not inlined | If variable is volatile, suppress. | #pragma warning(disable: 4510) function not inlined. |
C4725 | inline assembly instruction that may not produce accurate results on some Pentium microprocessors. | Now obselete. Suppress. | #pragma warning(disable: 4510) inaccurate results on some Pentium microprocessors? |
C4800 | int' : forcing value to bool 'true' or 'false' | Use a bool valued expression. Write out expressions, for example: "value >= 0" or "ptr != 0" (Boost prefers clarity to curtness). Take care if using templates that constants are of the right type, for example you may need "value == static_cast<T>(1)". Or use static_cast<bool>(expression). Or suppress. | # pragma warning(disable: 4800) int' : forcing value to bool 'true' or 'false' |
C4996 | 'putenv': The POSIX name for this item is deprecated (but NOT by POSIX!). | Suppress. | # pragma warning(disable: 4996) 'putenv' was declared deprecated. |
C4996 | ' this item is deprecated. | Many similar messages suggest using secure versions. Unless you strongly believe that the 'secure' versions are useful, suppress. WG14 Austin Group concerns may guide. See also _CRT_SECURE_NO_DEPRECATE and other defines above. | # pragma warning(disable: 4996) was declared deprecated. |
GCC
With GCC, it is more difficult to deal with warnings because there is no way (yet) locally silence individual warnings. It is considered important not to leave warnings switched off when meeting user code. This is an unresolved difficulty.
On the other hand, the number of unhelpful warnings seems fewer.
So more emphasis should be placed on fixing warnings, for example using static_cast, or providing missing items.
For a particular file,
#pragma GCC system_ No warnings from all this file, considered a system header.
or option --isystem
The -isystem command line option adds its argument to the list of directories to search for headers, just like -I. Any headers found in that directory will be considered system headers.
-Wsystem-headers
is effective until the end of the file.
Print warning messages for constructs found in system header files. Warnings from system headers are normally suppressed, on the assumption that they usually do not indicate real problems and would only make the compiler output harder to read. Using this command line option tells GCC to emit warnings from system headers as if they occurred in user code. However, note that using -Wall in conjunction with this option will not warn about unknown pragmas in system headers—for that, -Wunknown-pragmas must also be used.
The GCC docs say: "Also, while it is syntactically valid to put these pragmas anywhere in your sources, the only supported location for them is before any data or functions are defined. Doing otherwise may result in unpredictable results depending on how the optimizer manages your sources."
This might only mean you can't use them inside functions or class definitions, but it seems to imply that using them after a function definition leads to undefined behavior. Thus, even if we knew what setting to turn the warning back to (perhaps the warning wasn't enabled in the first place!), we might not be able to turn them back on.
<toolset>gcc:<cxxflags>-fdiagnostics-show-option # find out which options controls this warning (GCC >= 4.3)
#if defined(GNUC) GCC 4.3 and higher.
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
But that doesn't quite work, how do you know that the user wants -Wdeprecated-declarations to be set to "warning" after the include? Could be the user has disabled that one on the command line, in which case if we turn it back on, that's as annoying for the end user as warnings from Boost! There was a time when some MSVC std lib headers behaved like that, and believe me it was *seriously* annoying :-(
-Wall
This enables all the warnings about constructions that some users consider questionable, and that are easy to avoid (or modify to prevent the warning), even in conjunction with macros. This also enables some language-specific warnings described in C++ Dialect Options and Objective-C and Objective-C++ Dialect Options.
-Wall turns on the following warning flags:
-Waddress -Warray-bounds (only with -O2) -Wc++0x-compat -Wchar-subscripts -Wimplicit-int -Wimplicit-function-declaration -Wcomment -Wformat -Wmain (only for C/ObjC and unless -ffreestanding) -Wmissing-braces -Wnonnull -Wparentheses -Wpointer-sign -Wreorder -Wreturn-type -Wsequence-point -Wsign-compare (only in C++) -Wstrict-aliasing -Wstrict-overflow=1 -Wswitch -Wtrigraphs -Wuninitialized -Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-value -Wunused-variable -Wvolatile-register-var
Note that some warning flags are not implied by -Wall. Some of them warn about constructions that users generally do not consider questionable, but which occasionally you might wish to check for; others warn about constructions that are necessary or hard to avoid in some cases, and there is no simple way to modify the code to suppress the warning. Some of them are enabled by -Wextra but many of them must be enabled individually.
-Wextra
This enables some extra warning flags that are not enabled by -Wall. (This option used to be called -W. The older name is still supported, but the newer name is more descriptive.)
-Wclobbered -Wempty-body -Wignored-qualifiers -Wmissing-field-initializers -Wmissing-parameter-type (C only) -Wold-style-declaration (C only) -Woverride-init -Wsign-compare -Wtype-limits -Wuninitialized -Wunused-parameter (only with -Wunused or -Wall)
The option -Wextra also prints warning messages for the following cases:
- A pointer is compared against integer zero with
<',
<=',>', or
>='. - (C++ only) An enumerator and a non-enumerator both appear in a conditional expression.
- (C++ only) Ambiguous virtual bases.
- (C++ only) Subscripting an array which has been declared `register'.
- (C++ only) Taking the address of a variable which has been declared `register'.
- (C++ only) A base class is not initialized in a derived class' copy constructor.
More information needed here!
Individual Warnings
comparison between signed and unsigned integer expressions
-Wsign-compare - enable the warning (also turned on by -Wextra)
-Wno-sign-compare - disable the warning
Almost always points to a real issue. The ranges of these are obviously different, for example signed char -128 - 127 and unsigned char 0 - 255 for
range bit pattern
signed char -128 to 127 01111111 to 10000000 unsigned char 0 to 255 00000000 to 11111111
That means: signed char unsigned char
0 to 127 + +
128 to 255 - +
In the range from 128 to 255 a comparison from one to the other makes no sense. They can have the same bit pattern, yet test as not equal. This is the source of many subtle and hard to find bugs.
The same problem can occur with all of the integral types. For example with 16-bit short int unsigned ranges from -32768 to 32767, and the signed short ranges from 0 to 65535.
To fix, if you are sure that all the values for both things being compared fall into the overlap where their values are the same, you can static_cast one type to the other, if you must, but if you can change one of the types it's a better solution. If you are sure that you can fit all values into the compared range, i.e. positive numbers from 0 to (1<<(sizeof(IntegralType)*8-1))-1, it would make more sense to declare the signed one as an unsigned equivalent, (perhaps size_t). In general, if you don't intend for a variable to ever have negative values, take advantage of the diagnostic capabilities of the compiler and don't declare it as an unsigned type. After you've done this, if you see the warning again for one of these same variables, it is warning you of this same danger again.
This often shows up with loop counters. We all learned to use int, but if it's truly counting only positive numbers, size_t is better.
missing braces around initializer for ‘main()::X
-Wmissing-braces - enable the warning (also turned on by -Wall)
-Wno-missing-braces - disable the warning
This warning is trying to let you know that what you think you're initializing may not actually be what you're initializing.
struct X { int i; int j;}; struct Y { struct X x; int j; }; Y y={1,2}; return 0;
The above example initializes both the elements of X in Y, but doesn't initialize j. Change to:
Y y={{1},2};
to initialize half of X and all of Y, or better something like:
Y y={{1,2},3};
The same kind of problem can come up with:
int a[2][2]={ 0, 1, 2 };
versus:
int a[2][2]={ {0,1},2};
orint a[2][2]={ 0, {1,2}};
deprecated conversion from string constant to ‘char*’
-Wwrite-strings - enable this warning (disabled by default for C and enabled by default for C++) -Wno-write-strings - disable this warning
For C will warn about trying to modify string constants, but only if defined const. For C++ often symptomatic of a real bug since string literals are not writeable in C++.
This one shows up when you have a string literal, like "Hello world!" which is arguably const data, and you assign it no a non-const char* either directly or via passing as an argument to a functions which expects a non-const char *. both:
char* cp="Hello world!";and
void foo(char *cp); foo("Hello world")will get this warning because you're using a string literal where a char* is expected. If you say, so what? See if you can predict what will print in the example below.
#include <iostream> void foo(char *cp) { cp[0]='W'; } int main() { char *str="Hello, world"; foo(str); std::cout << str << std::endl; }
If you said that Wello, world will print, think again. Your most likely output is something like:
Segmentation fault (core dumped)
A string literal is put into a section that is read only and trying to write to it makes things blow up!
The right fix is to make anything that a string literal can be assigned to a const char*. Unfortunately, sometimes you can't fix the problem at the source, because it's someone else's code, and you can't change the declaration. For example, if you had this function:
int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict, char *format, char **kwlist, ...);
and you had a keyword list to pass it that looked like this:
static char *kwlist[] = {"fget", "fset", "fdel", "doc", 0}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO:property", kwlist, &get, &set, &del, &doc))
you would get the warning on the kwlist assignment, because you're assigning a const char * to char *, but when you correctly change it to:
static const char *kwlist[] = {"fget", "fset", "fdel", "doc", 0};
to get rid of the waring you'll get a warning on the call to PyArg_ParseTupleAndKeywords:
error: invalid conversion from ‘const char’ to ‘char’
because the library function was declared as taking a char for the fourth argument. Your only recourse is to cast away the constness with a const_cast:
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO:property", const_cast<char **>(kwlist), &get, &set, &del, &doc))
Annoying, yet more correct than before.
dereferencing type-punned pointer will break strict-aliasing rules
this warning is only active when strict-aliasing optimization is active
-Wstrict-aliasing - equivalent to -Wstring-aliasing=3 (included in -Wall)
-Wstrict-aliasing=N where N is in {1,2,3} - Higher N implies less false positives, at the expense of more work. Currently there are 3 levels.
Level 1: Most agressive, quick, very few false negatives, but many false positives. Might use if higher levels don't warn, but turning on -fstrict-aliasing breaks the code. Warns about pointer conversions even if pointer not dereferenced.
Level 2: Agressive, quick, not too precise. Less false positives than Level 1, but more false negatives. Only warns if pointer is dereferenced.
Level 3: Very few false positives and very few false negatives--the default.
-fstrict-aliasing - also turned on by -O2, -O3 and -Os. Tells the compiler that it's ok to do a certain class of optimization based on the type of expressions. In particular you're promising by using this flag that an object of one type won't reside at the same address as an object of an incompatible type. -fno-strict-aliasing - turns off this optimization. If this changes the behavior of your code, you have a problem in your code.
This is trying to let you know that you are asking the compiler to do undefined behavior and it may not do what you think it will do. As the optimization level increases, the liklihood that you won't like what it does will increase. I show a simple example later that surprisingly generates the wrong result when optimization at any level is turned on. Ignore this warning at your own peril. You are unlikely to care for the undefined behavior that results.
From the C++ Standard, section 3.10 Lvalues and rvalues 15 If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined — the dynamic type of the object, — a cv-qualified version of the dynamic type of the object, — a type similar (as defined in 4.4) to the dynamic type of the object, — a type that is the signed or unsigned type corresponding to the dynamic type of the object, — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object, — an aggregate or union type that includes one of the aforementioned types among its member (including, recursively, a member of a subaggregate or contained union), — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object, — a char or unsigned char type.
The following program generates 6 warnings about breaking strict-aliasing rules, and many would dismiss them. The correct output of the program is:
00000020 00200000
but when optimization is turned on it's:
00000020 00000020
THAT's what the warning is trying to tell you, that the optimizer is going to do things that you don't like. In this case seeing that acopy is set to a and never touched again, strict aliasing lets it optimize by just returning the original value of a at the end.
uint32_t swaphalves(uint32_t a) { uint32_t acopy=a; uint16_t *ptr=(uint16_t*)&acopy;// can't use static_cast<>, not legal. // you should be warned by that. uint16_t tmp=ptr[0]; ptr[0]=ptr[1]; ptr[1]=tmp; return acopy; } int main() { uint32_t a; a=32; cout << hex << setfill('0') << setw(8) << a << endl; a=swaphalves(a); cout << setw(8) << a << endl; }
Here's the (annotated) x86 assembler generated by gcc 4.4.1 for swaphalves, let's see what went wrong:
_Z10swaphalvesj: pushl %ebp movl %esp, %ebp subl $16, %esp movl 8(%ebp), %eax # get a in %eax movl %eax, -8(%ebp) # and store in in acopy leal -8(%ebp), %eax # now get eax pointing at acopy (ptr=&acopy) movl %eax, -12(%ebp) # save that ptr at -12(%ebp) movl -12(%ebp), %eax # get the ptr back in %eax movzwl (%eax), %eax # get 16 bits from ptr[0] in eax movw %ax, -2(%ebp) # store the 16 bits into tmp movl -12(%ebp), %eax # get the ptr back in eax addl $2, %eax # bump up by two to get to ptr[1] movzwl (%eax), %edx # get that 16 bits into %edx movl -12(%ebp), %eax # get ptr into eax movw %dx, (%eax) # store the 16 bits into ptr[1] movl -12(%ebp), %eax # get the ptr again leal 2(%eax), %edx # get the address of ptr[1] into edx movzwl -2(%ebp), %eax # get tmp into eax movw %ax, (%edx) # store into ptr[1] movl -8(%ebp), %eax # forget all that, return original a. leave ret
Scary, isn't it? Of course you could use -fno-strict-aliasing to get the right output, but the generated code won't be as good. A better way to accomplish the same thing without the warning's or the incorrect output is to define swaphalves like this:
uint32_t swaphalves(uint32_t a) { union swapem{ uint32_t a; uint16_t b[2]; }; swapem s={a}; uint16_t tmp; tmp=s.b[0]; s.b[0]=s.b[1]; s.b[1]=tmp; return s.a; }
The C++ compiler knows that members of a union alias, and this helps the compiler generate MUCH better code:
_Z10swaphalvesj: pushl %ebp # save the original value of ebp movl %esp, %ebp # point ebp at the stack frame movl 8(%ebp), %eax # get a in eax popl %ebp # get the original ebp value back roll $16, %eax # swap the two halves of a and return it ret
|? class does not have a virtual destructor Suppress or provide a virtual destructor. ??
Intel
Information needed here.
Darwin /MacOS
Information needed here.
ACC
Information needed here.
Borland
Information needed here.