Improve company productivity with a Business Account.Sign Up

  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 319
  • Last Modified:

Porting C to C++


I am in the process of moving a project from C to C++. We are talking about a project comprised of 10 C modules where only the main() function is in a C++ module.
This is a widely distrubted software running on many remote machines at nearly real-time environemt and performing very-high-priority tasks, thus I cannot make mistakes...
As the first step I renamed all the C modules to CPP and removed all the extern "C" from the code. The first thing that comes up is type-casting errors where C++ is more restrictive. Should I use the dynamic_cast and reinterpret_cast or rather the usual (type) cast?
Also any more vital advises you've got? (especially regarding how to prevent even the slightest decent in performance and stability)

Please respond ONLY if you had true experience with such an issue.

More info:
The reason I move from C to C++ is the compulsory need to use a C++ third-party code. I am not planning on changing the structure of my existing code.
The application relies stongly and deeply on alignment of the data structure. Should I be worried of alignment that is going to change in my sturcts?

  • 2
  • 2
  • 2
  • +1
1 Solution
>>The application relies stongly and deeply on alignment of the data structure. Should I be worried of
alignment that is going to change in my sturcts?

My biggest concern here would NOT be from C -> C++ but rather from one vendor's compiler to another's.  (As least I think that's what you are saying.)
The alignment issue can be solved with a compiler option
 (like /Zp1 in MS compilers).

I once had the task of converting legacy C code (I inherited (pun intended)) to be included with new C++ code.

The best advice I have is to only modify what's necessary.
I used the old-style casts where necessary.  There were not many.  I also re-evaluated the use of certain data types that required me to cast.  In the process, I cleared up some potential bugs.

Then I got curious and started wrappering the old functions into classes.  I learned a lot about the original design, but rewrote at least 25% of the code.

Later, that code had to be re-compiled for WinNT.  Luckily, I had made classes out of the old code.  It made the conversion (practically a rewrite) easier.  The conversion was pretty much a determination of what I could actually use.  Having 'like' grouped functionality in classes was a tremendous help.

Part of your decision has to do with your timeline, your familiarity with the original design and any future application for the code (or entire program).

If you're a true geek, you can compile and run the minimally changed code at work and do heavy experimentation at home.  Nothing to lose but time.
a) do you need to call the C++ code from inside the C code?if not, you can wrap everything that "remains C" in
extern "C" { /* C code here */ }

* Alignment: /Zp1 (align by one byte) can be a bad idea if the original compile used word or DWORD alignment *and* the code relies on this. I would run the "old C" code and trace the structure sizes. then place asserts in you cpp code like 'assert(sizeof("NastyStruct") == 47)'. oh, and make sure that the byte size of char is the same on both compilers.

* Bit packed structures: Bits can be your downfall. if the code uses sth like
struct Bitpacked {  
  int x : 4;
  int y : 3;

there are several issues: a) the size of the entire thingie (esp. when size is less than sizeof(int)), and b) the location of the individual bits. You typically can control a) by a compiler option, but with b) you can't do much except rewriting your code. Most compilers do pack such structures tight, but they are allowed to add pd bits - e.g. to move y to the beginning of the next full byte)

* Warning Level:
A good idea is to the maximum warning level (typically 4), and examine all warnings seriously. This may be tons if you come rom C, so just clean up all warnings at Level 3.

* ANSI compatibility
If the original code is ANSI compatible, check the cpp compiler for an option like "ANSI compatibility mode", or "disable Non-standard extensions".

* Type casts: you should get along with static_cast, and reinterpret_cast where necessary. dynamic_cast is a C++ class specific cast. However, you should understand all casts and the difference beetween them. In short:

static_cast : implicit casts that sometimes come with a warning (like loosing precision):

int i;
char c = (char) i;
char c = static_cast<char>(i);

reinterpret_cast: "hard" cast - "interpret these bits as if they were of type <foo>"

void * p;
long l = *(l *)p
long l = *(reinterpret_cast<long *>(p))

(BTW, I have the habit to use C-style casts throughout my c++ projects, but it' a good idea to check each cast when coming from C, since existing C code tends to rely on compiler  & platform dependent facts)

It's been a long time since I did C --> C++, but I'm looking forward to ASM ==> C++ next year ;)

Good luck, and happy testing
Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

I agree with peterchen's assessment on the /Zp1 thing.
It sounded as if I was suggesting to 1-byte align the code, but I should have said to match the byte alignment with the /Zp(n) option (if you use a compatible compiler).
mathoughtAuthor Commented:
I appreciate the advices, there is also one more issue. is it safe to replace all malloc() to new? Should I expect different behaviour once I move to new instead of malloc?
depends on what you call (or rather: your existing source) "different".

Practically, I would make this decision dependent on how much you expect the project to change *after* being revamped to C++. If you don't expect the project to grow very much, I'd strongly suggest stay with malloc/free, as the change is error prone and the only benefit you don't have the hassle of mixing delete and free later.

Technicaly, the following differences exist or can exist:

new/delete calls constructors & destructors on it's elements. Since they are most likely intrinsic vars structs that don't have such, there sould be no *real* difference

new is "type-aware". Although it's common to replace malloc(n) with ((void *)(new char[n]), and free(p) with delete ((char*)p), this has no "positive side effect" and it might be dead wrong on some compilers and platforms, for memory alignment and byte size of "char". It works on an x86 platform, though.

the actual memory allocated by new may differ (storing array lenght, using different allocation scheme,...) VC++' new calls malloc internally.

most compilers offer a non-standard "get allocation size" function for malloc, but not for new.

All these differences are negligible if the C code is well written - but the C I encountered (or wrote) often tends to rely on implementation specifics, so your milage may vary. Anyway, if your code ist that crude, it might not even survive the new compiler.

mathoughtAuthor Commented:
Thanks for the descriptive commnts, everyone.
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

  • 2
  • 2
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now