Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>First, you are fortunate to have a boss who recognizes that code refactoring can be a long-term cost-saving strategy.</p> <p>I've done this many times, that is, converting old C code to C++. The benefits may surprise you. The final code may be half the original size when you're done, and much simpler to read. Plus, you will likely uncover tricky C bugs along the way. Here are the steps I would take in your case. Small steps are important because you can't jump from A to Z when refactoring a large body of code. You have to go through small, intermediate steps which may never be deployed, but which can be validated and tagged in whatever RCS you are using.</p> <ol> <li><strong>Create a regression/test suite.</strong> You will run the test suite each time you complete a batch of changes to the code. You should have this already, and it will be useful for more than just this refactoring task. Take the time to make it comprehensive. The exercise of creating the test suite will get you familiar with the code. </li> <li><strong>Branch the project</strong> in your revision control system of choice. Armed with a test suite and playground branch, you will be empowered to make large modifications to the code. You won't be afraid to break some eggs.</li> <li><strong>Make those struct fields private.</strong> This step requires very few code changes, but can have a big payoff. Proceed one field at a time. <em>Try</em> to make each field <code>private</code> (yes, or protected), then isolate the code which access that field. The simplest, most non-intrusive conversion would be to make that code a <code>friend function</code>. Consider also making that code a method. Converting the code to be a method is simple, but you will have to convert all of the call sites as well. One is not necessarily better than the other. </li> <li><strong>Narrow the parameters to each function.</strong> It's unlikely that any function requires access to all 30 fields of the struct passed as its argument. Instead of passing the entire struct, pass only the components needed. If a function does in fact seem to require access to many different fields of the struct, then this may be a good candidate to be converted to an instance method.</li> <li><strong>Const-ify as many variables, parameters, and methods as possible.</strong> A lot of old C code fails to use <code>const</code> liberally. Sweeping through from the bottom up (bottom of the call graph, that is), you will add stronger guarantees to the code, and you will be able to identify the mutators from the non-mutators.</li> <li><strong>Replace pointers with references</strong> where sensible. The purpose of this step has nothing to do with being more C++-like just for the sake of being more C++-like. The purpose is to identify parameters that are never <code>NULL</code> and which can never be re-assigned. Think of a reference as a compile-time assertion which says, <em>this is an alias to a valid object and represents the same object throughout the current scope.</em></li> <li><strong>Replace <code>char*</code> with <code>std::string</code></strong>. This step should be obvious. You might dramatically reduce the lines of code. Plus, it's fun to replace 10 lines of code with a single line. Sometimes you can eliminate entire functions whose purpose was to perform C string operations that are standard in C++.</li> <li><strong>Convert C arrays to <code>std::vector</code> or <code>std::array</code></strong>. Again, this step should be obvious. This conversion is much simpler than the conversion from <code>char</code> to <code>std::string</code> because the interfaces of <code>std::vector</code> and <code>std::array</code> are designed to match the C array syntax. One of the benefits is that you can eliminate that extra <code>length</code> variable passed to every function alongside the array.</li> <li><strong>Convert <code>malloc</code>/<code>free</code> to <code>new</code>/<code>delete</code></strong>. The main purpose of this step is to prepare for future refactoring. Merely changing C code from <code>malloc</code> to <code>new</code> doesn't directly gain you much. This conversion allows you to add constructors and destructors to those structs, and to use built-in C++ automatic memory tools.</li> <li><strong>Replace localize <code>new</code>/<code>delete</code> operations with the <code>std::auto_ptr</code> family.</strong> The purpose of this step is to make your code exception-safe.</li> <li><strong>Throw exceptions wherever return codes are handled by bubbling them up.</strong> If the C code handles errors by checking for special error codes then returning the error code to its caller, and so on, bubbling the error code up the call chain, then that C code is probably a candidate for using exceptions instead. This conversion is actually trivial. Simply <code>throw</code> the return code (C++ allows you to throw any type you want) at the lowest level. Insert a <code>try{} catch(){}</code> statement at the place in the code which handles the error. If no suitable place exists to handle the error, consider wrapping the body of <code>main()</code> in a <code>try{} catch(){}</code> statement and logging it.</li> </ol> <p>Now step back and look how much you've improved the code, <em>without converting anything to classes</em>. (Yes, yes, technically, your structs are classes already.) But you haven't scratched the surface of <em>OO</em>, yet managed to greatly simplify and solidify the original C code. </p> <p><strong>Should you convert the code to use classes, with polymorphism and an inheritence graph?</strong> I say no. The C code probably does not have an overall design which lends itself to an OO model. Notice that the goal of each step above has nothing to do with injecting OO principles into your C code. The goal was to improve the existing code by enforcing as many compile-time constraints as possible, and by eliminating or simplifying the code.</p> <p>One final step.</p> <p><strong>Consider adding benchmarks</strong> so you can show them to your boss when you're done. Not just performance benchmarks. Compare lines of code, memory usage, number of functions, etc. </p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload