[cmake-developers] C++11 and target_compiler_feature proposal
Stephen Kelly
steveire at gmail.com
Thu Oct 10 20:07:53 EDT 2013
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 at 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 at 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 at 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 at 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.
More information about the cmake-developers
mailing list