Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

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.

source:wiki/cppwizard.png

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; }
};

Convert constructor and call super constructor

C++D
struct A : B {
    A() : B(1) { }
    A(int x) : B(x) { }
};
class A : B {
    this() { super(1); }
    this(int x) { super(x); }
};

Split declarations for variables with different types

C++D
int a = sizeof(void *);
int x, *y = (int*)null;
int a = (void *).sizeof;
int x;
int *y = cast(int *)null;

Add enumerator type to constants of named enumerator values

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;
}

Simple template translations

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; }

Preprocessor macros are converted to enum, alias or templates

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  n*n; }

Preprocessor conditionals are converted to static if or can be removed when specified in the "expansions" field

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;

It can deal with a number of complicated uses of preprocessor conditionals, but they can get much more difficult to read.

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;
}