Skip to content
PaskalCPP
C++

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.

cpp_mixing_std.pcpp
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.
output on x86_64-windows-gnu and x86_64-linux-gnu
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, @addincludepath and @copydll tell 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:

cpp_mixing_forms.pcpp
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.
output on x86_64-windows-gnu and x86_64-linux-gnu
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, const and type start a PaskalCPP block only in PaskalCPP's shape (the word, a name, then : or =): const int KC = 3; and using 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++.
  • exported exports PaskalCPP declarations only: before a C++ declaration or before cpp 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 header or source after cpp is 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 plain struct CPoint needs no cpp 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 source is taken, emitted in place, for C++ statements. They use the PaskalCPP variables in scope (LSum += LI in a C++ for), and a C++ local they declare is seen by the code after the block (cpp("LText")). cppstart header in a body is PR0051.
  • A cppend inside a C++ string or comment does not end the block. A block with no cppend is 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 #include or #define stays private to it, and its header after cpp 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.