C++ mixing
C++ is the surface. PaskalCPP and C++ sit side by side in the same source file: #include a header and call the library, with no import, binding or wrapper layer.
The surface
Any C or C++ library is used by #include-ing its header in the .pcpp file. Its names are opaque to PaskalCPP and checked by clang.
Here is the C++ standard library used straight from PaskalCPP: a std::string and a std::vector<int> as variables with their methods, std::sort, std::pow, and a C++ template written in the module and called from the PaskalCPP body.
module exe cpp_mixing_std;
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
template<typename T>
T Max2(T a, T b) {
return a > b ? a : b;
}
var
LS: std::string;
LV: std::vector<int>;
LD: float64;
LX: int32;
begin
LS := "hello world";
if LS.find("world") <> std::string::npos then
println("found at {}", LS.find("world"));
end;
LV.push_back(3);
LV.push_back(1);
LV.push_back(2);
std::sort(LV.begin(), LV.end());
println("sorted {} {} {}", LV[0], LV[1], LV[2]);
println("size {}", LV.size());
LX := 3;
LD := std::pow(float64(LX), 2.0);
LX := Max2(LX, int32(LD));
println("max {}", LX);
end.
found at 6
sorted 1 2 3
size 3
max 9
[Heap] Allocs: 0, Frees: 0, Leaked: 0
- Any C or C++ type is a type, written as C++ spells it: see C++ types.
- C++ is written inside an expression as in C++: see C++ in expressions.
@linklibrary,@addlibrarypath,@addincludepathand@copydlltell the build which library to link and where it lives: see Paths and libraries.- A mistake in mixed C++ is clang's (ZB0016), reported at the .pcpp line and column it is written on, in a C++ declaration and inside
cpp("...")alike.
Several forms from this page in one program (a C++ using, a ; and } inside a C++ string and comment, a # line in a C++ function, a struct from a cppstart header block, a C++ function calling a routine, keyword names after :: and ., and cpp("...")), built and run on both official targets:
module exe cpp_mixing_forms;
#include <cstring>
#include <ios>
using type = int;
int UseType() {
type LV = 5;
return LV;
}
const char* GS = "x; } begin end.";
/* ; } var X: int32 = 1; */
int After() {
return 7;
} // ; }
int Pick() {
#if 1
return 3;
#else
return 4;
#endif
}
struct CS {
int end;
int match;
};
cppstart header
struct CHdr {
int V;
};
cppend;
routine Twice(AN: int32): int32;
begin
return AN * 2;
end;
int FromCpp() {
return Twice(4);
}
var
LS: CS;
LH: CHdr;
begin
println("using {}", UseType());
println("{} {}", cpp("GS"), After());
println("pick {}", Pick());
LH.V := 9;
println("block {}", LH.V);
println("unqualified {}", FromCpp());
println("ios {}", int32(std::ios::end));
LS.end := 3;
LS.match := 4;
println("members {} {}", LS.end, LS.match);
println("escape {}", cpp("std::strlen(\"ab\")"));
println("paren {}", cpp("1 + 2") * 3);
end.
using 5
x; } begin end. 7
pick 3
block 9
unqualified 8
ios 2
members 3 4
escape 2
paren 9
[Heap] Allocs: 0, Frees: 0, Leaked: 0
C++ declarations at module level
At module level a token that starts no PaskalCPP declaration begins a C++ declaration, copied as written up to its end: a struct, a function, a template, an enum class, a using, a global, a C++ const. PaskalCPP code uses them all: a struct as a variable's type, its fields, the functions, the template, the global.
var,constandtypestart a PaskalCPP block only in PaskalCPP's shape (the word, a name, then:or=):const int KC = 3;andusing type = int;are C++,const KP: int32 = 4;is PaskalCPP.- A C++ string or comment is one whole token, so a
;, a}or a PaskalCPP word inside it never ends the C++. exportedexports PaskalCPP declarations only: before a C++ declaration or beforecpp header;it is PR0024.
The three forms
For the cases plain mixing cannot express there are three forms, each with one job.
cpp header; and cpp source;
Plain mixed C++ at module level goes to the module's generated C++ source. cpp header; sends the C++ that follows to the module's header, and cpp source; switches back.
- Both stand at module level only: in a body they are PR0050. Any word but
headerorsourceaftercppis PR0015. - PaskalCPP's own declarations do not follow the placement. An exported declaration goes to the header by its own rule; a private type goes to the source beside the C++ around it unless the header's own text names it, so a private
TPoint3 = record(CPoint)after a plainstruct CPointneeds nocpp header;.
cppstart ... cppend;
cppstart header|source ... cppend; holds C++ PaskalCPP does not parse, copied as written to the file it names, whatever the placement: macro-driven code (a #define in a header block, its use in a source block) and declarations that PaskalCPP code then uses (a struct in a header block as a variable's type).
- In a body only
cppstart sourceis taken, emitted in place, for C++ statements. They use the PaskalCPP variables in scope (LSum += LIin a C++for), and a C++ local they declare is seen by the code after the block (cpp("LText")).cppstart headerin a body is PR0051. - A
cppendinside a C++ string or comment does not end the block. A block with nocppendis LX0013.
cpp("...")
cpp("...") is a C++ expression written as a string, emitted in place and in parentheses: cpp("1 + 2") * 3 is 9. Its type is opaque, judged by clang.
- A plain string's C++ escapes are decoded first (
cpp("std::strlen(\"ab\")")is 2), and its value holds no line break (cpp("1\n+ 2")is PR0046,use a raw string). - A raw string,
cpp(R"(...)"), is taken as written and may span lines and hold quotes unescaped. - A prefixed string (
cpp(L"1")) is PR0009.
Preprocessor lines
A line whose first token is # (or its digraph %:) is a C++ preprocessor line, copied as written and never evaluated by PaskalCPP; a \ at its end continues it on the next line.
- At module level a
#line goes where the placement (cpp header; and cpp source;) puts the plain mixed C++ around it: the module's source by default, so a unit's own#includeor#definestays private to it, and its header aftercpp header;, so every module importing the unit sees it. In a body, and inside the braces of mixed C++, it stays where it is written. - A
#that is not the first token on its line is LX0008. - PaskalCPP's own conditional directives (
@ifdef) and C macros never see each other: see C preprocessor macros.
The module namespace
A module's declarations live in the C++ namespace pcpp_m_<module>, so a PaskalCPP name may be spelled like a C global (index, time, both declared by glibc) on every target.
- Mixed C++ in a module's own source names the module's declarations unqualified (
Twice(4)in a C++ function) or qualified (pcpp_m_cpp_namespace::Twice(4)). - C++ names an imported unit's export as
::pcpp_m_<unit>::<name>. A declaration the unit does not export is not in its header, so naming it from C++ is clang's error.
Names spelled like keywords
A C/C++ name spelled like a PaskalCPP keyword is reached with a leading ::: ::match(4) and ::until(3) call the C++ functions match and until. After :: and . any keyword is a name: std::ios::end, and a C++ struct's fields end and match (LS.end := 3).
Calling in both directions
A routine marked clink has C linkage and its plain name, so C code calls it, and a PaskalCPP program binds a C routine with clink and external. Objects meet C++ classes in both directions too: see Linkage and Objects and C++ classes.