Brad King wrote: > On 10/09/2013 10:44 AM, Stephen Kelly wrote: >> if(NOT needs17 EQUAL -1) >> set(standard 17) >> elseif(NOT needs14 EQUAL -1) >> set(standard 14) >> elseif(NOT needs11 EQUAL -1) >> set(standard 11) >> endif() >> >> # ... >> set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD ${standard}) > > This assumes a linear ordering among standards, which is true for the > actual standards, but may not be true for the compiler feature levels. > For example, the GNU compiler has a "gnu" variant branching off from > each standard level.
Right, it is not really linear. One of my dependencies might require some extension feature like 'gnu_template_instantiation' feature, which we'd list as being a -std=gnu++98 feature. http://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html#Template-Instantiation Another dependency might require variadic_templates, which is -std=c++11. So, our calculation should result in -std=gnu++11, assuming extensions are either consistently available between standard version specifications or subsumed by more-recent standards. Are any other compilers as 'interesting' or have extensions like that? Interestingly, clang supports rvalue references in c++98 mode as an extension, but GCC does not: http://clang.llvm.org/docs/LanguageExtensions.html stephen@hal:~/dev/src/playground/cpp{master}$ clang++ main.cpp main.cpp:71:18: warning: rvalue references are a C++11 extension [-Wc++11- extensions] int function(int &&i) ^ 1 warning generated. stephen@hal:~/dev/src/playground/cpp{master}$ clang++ -std=c++98 main.cpp main.cpp:71:18: warning: rvalue references are a C++11 extension [-Wc++11- extensions] int function(int &&i) ^ 1 warning generated. stephen@hal:~/dev/src/playground/cpp{master}$ clang++ -std=gnu++98 main.cpp main.cpp:71:18: warning: rvalue references are a C++11 extension [-Wc++11- extensions] int function(int &&i) ^ 1 warning generated. stephen@hal:~/dev/src/playground/cpp{master}$ g++ -std=gnu++98 main.cpp main.cpp:71:18: error: expected ‘,’ or ‘...’ before ‘&&’ token main.cpp: In function ‘int function(int)’: main.cpp:73:10: error: ‘i’ was not declared in this scope This means that if(CLANG), we could list rvalue references as a c++98 feature, and specify -Wno-c++11-extensions as the flag. However, I don't think that's a good idea. I just mention it for completeness. Similarly, both clang and GCC accept variadic templates by default, also with a warning. For anything that warns, I suggest we consider it not-available. So, I think this is a two dimensional calculation. There is a standard axis and a extension axis for the compiler flag, for GCC and clang at least. That means that we could do something like this: if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) list(APPEND CMAKE_CXX11_COMPILER_FEATURES final override ) endif() if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) list(APPEND CMAKE_CXX14_COMPILER_FEATURES generalized_lambda_capture return_type_deduction ) endif() # TODO: Add an appropriate GCC version check list(APPEND CMAKE_CXX98_COMPILER_EXTENSIONS gnu_template_instantiation ) # TODO: Add an appropriate GCC version check list(APPEND CMAKE_CXX14_COMPILER_EXTENSIONS gnu_generalized_lambda_capture_no_auto ) Assuming a c++14 only gnu_generalized_lambda_capture_no_auto extension to allow non-use of auto, as described in section 5.1 here http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf but not part of the draft standard. Then, in the implementation of target_compiler_feature, list(FIND CMAKE_CXX11_COMPILER_FEATURES ${FEATURE_NAME} needs11) list(FIND CMAKE_CXX11_COMPILER_EXTENSIONS ${FEATURE_NAME} needs11ext) list(FIND CMAKE_CXX14_COMPILER_FEATURES ${FEATURE_NAME} needs14) list(FIND CMAKE_CXX14_COMPILER_EXTENSIONS ${FEATURE_NAME} needs14ext) if(NOT needs14 EQUAL -1) set(standard 14) elif(NOT needs14ext EQUAL -1) set(standard 14) set(extension TRUE) elseif(NOT needs11 EQUAL -1) set(standard 11) elseif(NOT needs11ext EQUAL -1) set(standard 11) set(extension TRUE) endif() # ... set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD ${standard}) set_property(TARGET ${TARGET_NAME} PROPERTY CXX_EXTENSION ${extension}) Then, in cmLocalGenerator: const char *standard = target->GetProperty("CXX_STANDARD"); bool ext = target->GetPropertyAsBool("CXX_EXTENSION"); const char *type = ext ? "EXTENSION" : "STANDARD"; std::string compile_option = "CMAKE_CXX" + std::string(standard) + "_" + std::string(type) + "_COMPILE_OPTION"; if (const char *opt = target->GetMakefile()->GetDefinition(compile_option.c_str())) { this->AppendFlags(flags, opt); } And: if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11") set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11") endif() if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9 # AND VERSION_LESS 4.11 # Speculative ) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++1y") set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++1y") endif() > Perhaps each feature can map to the minimum standard spec flag > it needs: > > if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) > set(CMAKE_CXX_FEATURE_FLAG_final -std=c++11) > set(CMAKE_CXX_FEATURE_FLAG_override -std=c++11) > endif() > if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) > set(CMAKE_CXX_FEATURE_FLAG_generalized_lambda_capture -std=c++1y) > set(CMAKE_CXX_FEATURE_FLAG_return_type_deduction -std=c++1y) > endif() > > Then have a graph of flags subsuming others: > > set(CMAKE_CXX_STANDARD_SUBSUMES_-std=c++1y -std=c++11) While this looks like something that would work too, I'd prefer what I described above, as it uses a 'more-standard' COMPILE_OPTIONS, and the above ends up having to specify -std=c++11 N times for each feature or hide it with a macro. I think what I proposed above is more readable. > > Then from the needed features, their corresponding flags, > and the "subsume" graph ... and the compiler version. Currently the default for GCC is gnu++98. I assume that will be updated to gnu++11 at some point, and then we will not have to add any flag at all, and we would be 'wrong' to add -std=c++11 because that does not include extensions. I wonder if we should always use the extension flag, unless otherwise specified with set(CMAKE_STRICT_CXX_STANDARD ON)? Because gnu++98 is the GCC default, someone could be using 'gnu template instantiation' in existing code, and then uses CMake to specify the need for variadic_templates, so we add -std=c++11 and boom - no more gnu extensions. At least, as this is a new feature, there would need to be some user involvement when the error appears by either specifying the feature or upgrading a dependency which newly specifies a feature requiring a flag. > , compute the possible flags. > Then provide a way for the project and/or user to specify > their preferred flag and use it or error if it is not one > of those possible. If no preference is given, choose the > "oldest" flag. I don't think this is necessary, and I'd like to keep this away from the user as much as possible. Thanks, Steve. -- Powered by www.kitware.com Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ Follow this link to subscribe/unsubscribe: http://public.kitware.com/cgi-bin/mailman/listinfo/cmake-developers