Hello, world!
graph LR
A["` hello.cpp**2** `"] ==> B(["` **cppfront** `"]);
B ==> C[hello.cpp];
C ==> D([Your favorite<br> C++ compiler<p>... and IDE / libraries / build<br>system / in-house tools / ...]);
A hello.cpp2
program
Here is the usual one-line starter program that prints Hello, world!
. Note that this is a complete program, no #include
required:
But let's add a little more, just to show a few things:
main: () = {
words: std::vector = ( "Alice", "Bob" );
hello( words[0] );
hello( words[1] );
}
hello: (msg: std::string_view) =
std::cout << "Hello, (msg)$!\n";
This short program code already illustrates a few Cpp2 essentials.
Consistent context-free syntax. Cpp2 is designed so that there is one general way to spell a given thing, that works consistently everywhere. All Cpp2 types/functions/objects/namespaces are written using the unambiguous and context-free declaration syntax "name :
kind =
statement". The :
is pronounced "is a," and the =
is pronounced "defined as."
-
main
is a function that takes no arguments and returns nothing, and is defined as the code body shown. -
words
is astd::vector
, initially defined as holding"Alice"
and"Bob"
. -
hello
is a function that takes astd::string_view
it will only read from and that returns nothing, and is defined as code that prints the string tocout
the usual C++ way.
All grammar is context-free. In particular, we (the human reading the code, and the compiler) never need to do name lookup to figure out how to parse something — there is never a "vexing parse" in Cpp2. For details, see Design note: Unambiguous parsing.
Simple, safe, and efficient by default. Cpp2 has contracts (tracking draft C++26 contracts), inspect
pattern matching, string interpolation, automatic move from last use, and more.
-
Declaring
words
uses "CTAD" (C++'s normal constructor template argument deduction) to deduce the type of elements in thevector
. -
Calling
words[0]
andwords[1]
is bounds-checked by default. From Cpp2 code, ordinarystd::vector
subscript accesses are safely bounds-checked by default without requiring any upgrade to your favorite standard library, and that's true for any similar subscript of something whose size can be queried usingstd::size()
andstd::ssize()
, and for whichstd::begin()
returns a random access iterator, including any in-house integer-indexed container types you already have that can easily providestd::size()
andstd::ssize()
if they don't already. -
hello
uses string interpolation to be able to write"Hello, (msg)$!\n"
instead of"Hello, " << msg << "!\n"
. String interpolation also supports standard C++ format specifications, so you won't need iostream manipulators.
Simplicity through generality + defaults. A major way that Cpp2 delivers simplicity is by providing just one powerful general syntax for a given thing (e.g., one function definition syntax), but designing it so you can omit the parts you're not currently using (e.g., where you're happy with the defaults). We're already using some of those defaults above:
-
We can omit writing the
-> void
return type for a function that doesn't return anything, as both of these functions do. -
We can omit the
{
}
around single-statement function bodies, ashello
does. -
We can omit the
in
on themsg
parameter. Cpp2 has just six ways to pass parameters: The most common ones arein
for read-only (the default so we can omit it, ashello
does), andinout
for read-write. The others arecopy
,out
,move
, andforward
.
For details, see Design note: Defaults are one way to say the same thing.
Order-independent by default. Did you notice that main
called hello
, which was defined later? Cpp2 code is order-independent by default — there are no forward declarations.
Seamless compatibility and interop. We can just use std::cout
and std::operator<<
and std::string_view
directly as usual. Cpp2 code works with any C++ code or library, including the standard library, using ordinary direct calls without any wrapping/marshaling/thunking.
C++ standard library is always available. We didn't need #include <iostream>
or import std;
. The full C++ standard library is always available by default if your source file contains only syntax-2 code and you compile using cppfront's -p
(short for -pure-cpp2
), or if you use -im
(short for -import-std
). Cppfront is regularly updated to be compatible with C++23 and the latest draft C++26 library additions as soon as the ISO C++ committee votes them into the C++26 working draft, so as soon as you have a C++ implementation that has a new standard (or bleeding-edge draft standard!) C++ library feature, you'll be able to fully use it in Cpp2 code.
Building hello.cpp2
Now use cppfront
to compile hello.cpp2
to a standard C++ file hello.cpp
:
The result is an ordinary C++ file that looks like this: 1
Here we can see more of how Cpp2 makes its features work.
How: Consistent context-free syntax.
- All compiled lines are portable C++20 code we can build with pretty much any C++ compiler released circa 2019 or later. Cpp2's context-free syntax converts directly to today's Cpp1 syntax. We can write and read our C++ types/functions/objects in simpler Cpp2 syntax without wrestling with context sensitivity and ambiguity, and they're all still just ordinary types/functions/objects.
How: Simple, safe, and efficient by default.
- Line 9: CTAD just works, because it turns into ordinary C++ code which already supports CTAD.
- Lines 10-11: Automatic bounds checking is added to
words[0]
andwords[1]
nonintrusively at the call site by default. Because it's nonintrusive, it works seamlessly with all existing container types that arestd::size
andstd::ssize
-aware, when you use them from safe Cpp2 code. - Line 11: Automatic move from last use ensures the last use of
words
will automatically avoid a copy if it's being passed to something that's optimized for rvalues. - Line 15: String interpolation performs the string capture of
msg
's current value viacpp2::to_string
. That usesstd::to_string
when available, and it also works for additional types (such asbool
, to printfalse
andtrue
instead of0
and1
, without having to remember to usestd::boolalpha
).
How: Simplicity through generality + defaults.
- Line 7:
in
parameters are implemented usingcpp2::in<>
, which is smart enough to pass byconst
value when that's safe and appropriate, otherwise byconst&
, so you don't have to choose the right one by hand.
How: Order-independent by default.
- Lines 5 and 7: Order independence happens because cppfront generates all the type and function forward declarations for you, so you don't have to. That's why
main
can just callhello
: both are forward-declared, so they can both see each other.
How: Seamless compatibility and interop.
- Lines 9-11 and 15: Ordinary direct calls to existing C++ code, so there's never a need for wrapping/marshaling/thunking.
How: C++ standard library always available.
- Lines 1 and 3:
std::
is available because cppfront was invoked with-p
, which implies either-im
(short for-import-std
) or-in
(short for-include-std
, for compilers that don't support modules yet). The generated code tellscpp2util.h
toimport
the entire standard library as a module (or do the equivalent via headers if modules are not available).
Building and running hello.cpp
with any recent C++ compiler
Finally, just build hello.cpp
using your favorite C++20 compiler, where CPPFRONT_INCLUDE
is the path to /cppfront/include
:
> cl hello.cpp -std:c++20 -EHsc -I CPPFRONT_INCLUDE
> hello.exe
Hello, world!
$ g++ hello.cpp -std=c++20 -ICPPFRONT_INCLUDE -o hello
$ ./hello.exe
Hello, world!
$ clang++ hello.cpp -std=c++20 -ICPPFRONT_INCLUDE -o hello
$ ./hello.exe
Hello, world!
➤ Next: Adding cppfront to your existing C++ project
-
For presentation purposes, this documentation generally shows the
.cpp
as generated when using cppfront's-c
(short for-clean-cpp1
), which suppresses extra information cppfront normally emits in the.cpp
to light up C++ tools (e.g., to let IDEs integrate cppfront error message output, debuggers step to the right lines in Cpp2 source code, and so forth). In normal use, you won't need or even want-c
. ↩