C++ Conversion Wizard
Visual D can help you converting your C++ code base to D. Even though it is capable of some quite extensive reorganization of the code, don't expect to get compilable and runnable code out of the box. Helping the converter with additional replacement rules and manual additional editing of the result might be necessary.
The conversion consists of a number of steps:
- convert text into a stream of tokens with preceding white space or comments
- convert/expand preprocessor directives, so that the text is no longer a mixture of two languages. Conditionals are replaced by constructs that should fit into a tree hierarchy similar to "static if" in D.
- apply user specified replacement rules (pre)
- parse the text with a (slightly sloppy) C++ parser into a syntax tree
- perform a number of transformations on the tree
- apply user specified replacement rules (post)
- write output including adding a prefix to D keywords that are not C++ keywords
You can invoke the C++ conversion wizard from the Visual D menu.

Options:
- Convert: Input files/Current document/Current selection. When selecting "Input files", a whole source code base can be converted, writing files to the output directory. Otherwise, the text in the editor is replaced. If you have not changed the text and selection after the last conversion, redoing the conversion will use the initial code to better allow tweaking options.
- Input dir: base directory of input file specifications
- Files and directories: filename pattern to include into conversion, prefix with '+' to recurse into subdirectories, prefix with '-' to exclude files from the list of files specified so far.
- Source code header: header to write into output files. The identifier MODULENAME is replaced with the module name of the generated D file. If it is not specified in the header, a module statement will be prepended to it.
- Output dir: the directory where to write output files to.
- Write intermediate files: To debug problems during the conversion, intermediate files can be written to sub directories "pass1" (after the proprocessor handling) and "pass2" (after the code reorganization, but before the final replacements).
- Preprocessor expansions: only the preprocessor identifiers given here will be expanded. All other preprocessor directives are translated to corresponding D code. If no assignment is given, the first definition within the input source code is used. If preprocessor conditionals are expanded, only the active code path is written to the output file.
- Version conditionals: normally, preprocessor conditionals are translated to "static if" conditions, but if an identifier is specified here, version conditionals are used.
- Pre and post token replacements: some constructs are not parsable or
don't convert well. With the token replacements you can inject some replacement
rules into the conversion process. The replacement follows the
Token Search and Replace functionality. The syntax of the rule is
[filename-pattern::] search-pattern => replacement
If you want to span the rule across multiple lines, use the \ line splicing mechanism. - Value/reference types: sometimes the parser or the converter stumbles across unknown identifiers that might be types. You can help it specifying them here. Value or reference semantics determine whether an indirection is removed from pointer declarations.
- Keyword prefix: identifiers that are keywords in D, but not in C++, are prefixed with the given text.
- Package prefix: module names are prefixed with the given package identifier
- Load/Save: save and restore the current dialog settings
- Convert: start the conversion
When using the converter on the current selection (there is also a command VisualD.ConvertSelection that you can assign to a keyboard shortcut) you should select complete declarations, e.g. variable or class declarations and functions. Otherwise the parser will stumble.
This tool is the result of generalizing an attempt to convert the dmd front end code to D, so please don't be surprised to find some unexpected special casings from time to time. The result of that attempt was that the converted code was parsable, but did not pass all semantic stages.
Running from the console
There is also a command line version of this wizard available in the downloads folder: cpp2d.exe
You can pass a config file to the converter that has been saved by the wizard dialog.
cpp2d -config myoptions.c2d [files-specs...]If you pass files to the program, file specifications in the configuration file are ignored.
See here for an example of a configuration file to get started on converting the dmd front end.
Examples
Here are some examples of what the converter can do for you:
Move member and field implementation into class declaration.C++ | D |
---|---|
class C { // comments are usually kept static int x; DECL *foo(int flags = 0); }; int C::x = 3; // moved with the implementation DECL *C::foo(int flags) { return NULL; } | struct C { // comments are usually kept static int x = 3; // moved with the implementation DECL *foo(int flags = 0) { return null; } }; |
C++ | D |
---|---|
struct A : B { A() : B(1) { } A(int x) : B(x) { } }; | class A : B { this() { super(1); } this(int x) { super(x); } }; |
C++ | D |
---|---|
int a = sizeof(void *); int x, y = (int)null; | int a = (void *).sizeof; int x; int *y = cast(int *)null; |
C++ | D |
---|---|
typedef enum { e1, e2 } EX; enum ENUM { kEnum }; int foo() { Ident id = (op == kEnum) ? 0 : 1; } | enum unnamed_1 { e1, e2 }alias unnamed_1 EX; enum ENUM { kEnum }; int foo() { Ident id = (op == ENUM.kEnum) ? 0 : 1; } |
C++ | D |
---|---|
template<typename TYPE> struct ArrayBase : Array { TYPE* data; }; template<typename S, class T> double foo(S s, T t) { return s+t; } | class ArrayBase( TYPE) : Array { TYPE* data; }; double foo( S, T)(S s, T t) { return s+t; } |
C++ | D |
---|---|
#define K1 1 // keep comment #define K2 (K1+K1) #define K3 K1 #define SQR(n) (n)(n) , enum K1 = 1; // keep comment enum K2 = (K1+K1); alias K3 K1; auto SQR( ARG1 )(ARG1 n) { return nn; } | #define K1 1 // keep comment #define K2 (K1+K1) #define K3 K1 #define SQR(n) (n)(n) , enum K1 = 1; // keep comment enum K2 = (K1+K1); alias K3 K1; auto SQR( ARG1 )(ARG1 n) { return nn; } |
C++ | D |
---|---|
#define ABC 1 a = 1; #if ABC b = 2; #endif c = 3; | enum ABC = 1; a = 1; static if(ABC) { b = 2; } c = 3; |
C++ | D |
---|---|
void foo(x) { if (x) c = 0; #if COND1 // comment else if (a) #elif COND2 else if (b) #endif c = 1; } | void foo(x) { if (x) c = 0; else if(true) static if(COND1) // comment /else/ if (a) goto L_T4; else goto L_F4; else static if(COND2) /else/ if (b) goto L_T4; else goto L_F4; else goto L_T4; else L_F4: if(0) L_T4: c = 1; } |