hsutter / cppfront

A personal experimental C++ Syntax 2 -> Syntax 1 compiler

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[SUGGESTION] Conditional compilation support (`static if`?)

bluetarpmedia opened this issue · comments

Suggestion

Cppfront could support conditional compilation where unselected branches are dropped and not lowered to Cpp1.

This would be useful for at least two cases:

1. When a Cpp2 user wants to target multiple C++ standards, e.g. C++20 and C++23

The following example shows the intention but doesn't work, because if constexpr does not remove the unselected branch.

print: (message: std::string) = {
  if constexpr __cplusplus == 202002L {        // C++20
    std::cout << message << "\n";
  }
  else if constexpr __cplusplus >= 202100L {   // C++23 and beyond
    std::println(message);  // <-- Error after lowering and compiling for C++20
  }
}

2. OS / platform differences

Cross-platform software usually needs an abstraction layer somewhere to hide differences in APIs or CPU architecture, e.g.:

create_memory_map: () = {
  if /* Windows */ {
    MapViewOfFile(...);   // Win32 API
  }
  else if /* POSIX */ {
    mmap(...);            // POSIX API
  }
}

Today's workarounds in C++:

  • Use #ifdefs in a single source file
  • Conditionally build different source files for the target platform (e.g. mmap_windows.cpp or mmap_posix.cpp) which use a common header file

Both of those are common solutions in today's C++ and have different trade-offs. The second is usually a better architectural solution where there are many differences. But the first solution is attractive because it keeps the related source code together, and also enables inlining without requiring whole program / link-time optimization to inline across translation units.

The second solution can become problematic when there are different build systems for different platforms (e.g. a Visual Studio solution for Windows and Xcode project for macOS), since the alternative source file is not usually included in the current platform's project.

The second solution remains an option in Cpp2. Using the print example above, the user's build system can have different targets and conditionally compile print_20.cpp2 for C++20 or print_23.cpp2 for C++23.

But having conditional compilation support in a single source file is still useful for both inlining opportunities and cohesion.

One option is to follow D's example with its static if and its related version feature.

version (Windows) = {

}

version (macOS) = {

}

// Note: Despite the braces, this does not introduce a new scope.

This would require the cppfront compiler to take some new arguments to control lowering to Cpp1. These are just examples to show the intention, but something like:

  • version ("Win32", "Win64", "macOS", etc)
  • cpp_standard ("20", "23", etc)

This is how the earlier print example could look with static if:

print: (message: std::string) = {
  static if (Compiler.cpp_standard == 20) {
    std::cout << message << "\n";
  }
  else static if (Compiler.cpp_standard == 23) {
    std::println(message);
  }
}

When lowered to Cpp1 (after running Cppfront with -cpp_standard=20):

void print(cpp2::in<std::string> message) {
  std::cout << message << "\n";  // Note: There are no new scopes introduced
}

Adding these new arguments (version, cpp_standard) would change cppfront's current behaviour. Currently, the lowered Cpp1 can be considered agnostic regarding C++ standards and platform differences, and the Cpp1 code could even be committed into source control. However, I don't view that as a property that should be pursued or guaranteed. We don't commit object files produced by a C++ compiler into source control, for example, and one day Cppfront may lower to an immediate representation instead of to C++.

Further, it's only a matter of time (hopefully!) before C++26 and C++29 emerge. For as long as cppfront will lower to C++, I expect users to eventually want to choose what kind of C++ is emitted (just like with the std::cout vs std::println example).

Will your feature suggestion eliminate X% of security vulnerabilities of a given kind in current C++ code?

No.

Will your feature suggestion automate or eliminate X% of current C++ guidance literature?

Yes, see C++ Core Guidelines: ES.30: Don’t use macros for program text manipulation

In the future, static reflection is likely to eliminate the last needs for the preprocessor for program text manipulation.

Describe alternatives you've considered.

if constexpr remains very useful but is not the correct solution when only the selected branch should be compiled.

Cpp2 metafunctions are an interesting option but, if I understand correctly, only operate on types. Further, metafunctions seem perfectly designed for reusability (e.g. interface, struct, polymorphic, etc), rather than conditional code which will be very specific to the user's problem.

I strongly believe that there should a be strong separation of compile time and run time language constructs. In the end for the sake of simplicity and common sense all compile time constructs must have common syntax like meta functions do.
There are already discussions exist on this, I.e.
#644

The new placeholder variable _ in C++26 is an example where adding a cpp_standard flag to cppfront to control the flavour of C++ that is emitted would be useful.

(This is not related to static if itself, but rather the cpp_standard flag which was proposed as part of static if.)

Currently cppfront transforms its Cpp2 placeholder _ into C++20-compatible code by introducing new names, e.g. auto_1, auto_2 etc.

But if cppfront had a cpp_standard flag then users who want to target C++26 could run cppfront -cpp_standard=26 and generate C++26 _ placeholders in the resulting code.