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

Reply via email to