Strategies to upgrade antiquated C and C++ code to some more modern coding practice.
-
I'm deep into some antiquated C and C++ code (some here would say antediluvian ) I'd like to start instructing my team to clean up code as they go. We have ton of low level libraries that cannot be easily updated (time and budget concerns) And from what I can see, they work. Do you know of any documents and/or white papers that discuss this particular topic ? For now, I'm telling people to use
reference
instead of passing everything by pointers and useconst
as much as possible. To at least indicate code intent. I can't even use STL (string and vector) at this point. :-(CI/CD = Continuous Impediment/Continuous Despair
I am also working in an old C++ MFC project that was developed in the late 90's. I am using Reshaper from JetBrains. It gives a lot of suggestions when looking at old C++ code. Adding a 'const' shows up a lot. It includes a lot of suggestions to use the newer memory/string functions that require providing the buffer size. I don't take all of it's suggestions since the code is very reliable.
-
I'm deep into some antiquated C and C++ code (some here would say antediluvian ) I'd like to start instructing my team to clean up code as they go. We have ton of low level libraries that cannot be easily updated (time and budget concerns) And from what I can see, they work. Do you know of any documents and/or white papers that discuss this particular topic ? For now, I'm telling people to use
reference
instead of passing everything by pointers and useconst
as much as possible. To at least indicate code intent. I can't even use STL (string and vector) at this point. :-(CI/CD = Continuous Impediment/Continuous Despair
I have done this many times, doing it now for my current company. Old C code from the early 90s written to run on another OS (embedded) tweaked and tweaked into unmaintainability extreme. At the end of each phase the programming should produce exactly the same output/changes. My description here is for C, what I am working on now; it will work for any language(s). Phase 1 - find and deal with global variables. This is a judgement call as some things are properly global, some are just laziness on the part of some programmer. One tactic, make a structure, move them there to get them all organized. Refer to the globals only as part of the structure and pass only pointers to the structure. Control, you want to get control of the globals or at least make it clear where they are getting changed. In C/C++ a function that gets the structure read only will be "const", if it changes a value then not const, get it? Phase 2 - find duplicate (or near duplicate) code and create functions or subs to perform. Replace this code with calls. I call this "mining functions', you are digging them out of the code. Repeat till there are not more easy targets to mine. You can repeat this later in the process. Phase 3 - now look for unexecutable code - in large systems there will often be some. In the system I am working on now there were subs that never get called, functions that got called but the return values were ignored or tossed away. These really were changing global variables - that would be discovered in Phase 2, right? Phase 4 - look for bad algorithms and processes. for loops, while loops are the first targets. Repeat until you've done enough. Each phase makes it easier to see what to do int the next.
-
I'm deep into some antiquated C and C++ code (some here would say antediluvian ) I'd like to start instructing my team to clean up code as they go. We have ton of low level libraries that cannot be easily updated (time and budget concerns) And from what I can see, they work. Do you know of any documents and/or white papers that discuss this particular topic ? For now, I'm telling people to use
reference
instead of passing everything by pointers and useconst
as much as possible. To at least indicate code intent. I can't even use STL (string and vector) at this point. :-(CI/CD = Continuous Impediment/Continuous Despair
Michael Feathers wrote a great book "Working with Legacy Code" for exactly this. Step 1: wrap anything you are going to change in tests so you know what it does now and can ensure it continues to do that after your changes... Step 2...whatever is necessary to fix problems you are having. The strangler fig pattern can be helpful - gradually wrap/replace sections until the whole codebase has been replaced, like a strangler fig strangling a tree. Ian
-
I have done this many times, doing it now for my current company. Old C code from the early 90s written to run on another OS (embedded) tweaked and tweaked into unmaintainability extreme. At the end of each phase the programming should produce exactly the same output/changes. My description here is for C, what I am working on now; it will work for any language(s). Phase 1 - find and deal with global variables. This is a judgement call as some things are properly global, some are just laziness on the part of some programmer. One tactic, make a structure, move them there to get them all organized. Refer to the globals only as part of the structure and pass only pointers to the structure. Control, you want to get control of the globals or at least make it clear where they are getting changed. In C/C++ a function that gets the structure read only will be "const", if it changes a value then not const, get it? Phase 2 - find duplicate (or near duplicate) code and create functions or subs to perform. Replace this code with calls. I call this "mining functions', you are digging them out of the code. Repeat till there are not more easy targets to mine. You can repeat this later in the process. Phase 3 - now look for unexecutable code - in large systems there will often be some. In the system I am working on now there were subs that never get called, functions that got called but the return values were ignored or tossed away. These really were changing global variables - that would be discovered in Phase 2, right? Phase 4 - look for bad algorithms and processes. for loops, while loops are the first targets. Repeat until you've done enough. Each phase makes it easier to see what to do int the next.
Thanks.
CI/CD = Continuous Impediment/Continuous Despair
-
Michael Feathers wrote a great book "Working with Legacy Code" for exactly this. Step 1: wrap anything you are going to change in tests so you know what it does now and can ensure it continues to do that after your changes... Step 2...whatever is necessary to fix problems you are having. The strangler fig pattern can be helpful - gradually wrap/replace sections until the whole codebase has been replaced, like a strangler fig strangling a tree. Ian
Thanks for the book reference.
CI/CD = Continuous Impediment/Continuous Despair
-
I'm deep into some antiquated C and C++ code (some here would say antediluvian ) I'd like to start instructing my team to clean up code as they go. We have ton of low level libraries that cannot be easily updated (time and budget concerns) And from what I can see, they work. Do you know of any documents and/or white papers that discuss this particular topic ? For now, I'm telling people to use
reference
instead of passing everything by pointers and useconst
as much as possible. To at least indicate code intent. I can't even use STL (string and vector) at this point. :-(CI/CD = Continuous Impediment/Continuous Despair
Changing pointers to references may have very interesting consequences. Before I'd be making API changes like that, I would want a solid set of test cases to prove I hadn't broken anything. - glancing at project on new laptop with 20 year old mfc code and limited comments.
Charlie Gilley “They who can give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety.” BF, 1759 Has never been more appropriate.
-
Michael Feathers wrote a great book "Working with Legacy Code" for exactly this. Step 1: wrap anything you are going to change in tests so you know what it does now and can ensure it continues to do that after your changes... Step 2...whatever is necessary to fix problems you are having. The strangler fig pattern can be helpful - gradually wrap/replace sections until the whole codebase has been replaced, like a strangler fig strangling a tree. Ian
-
I'm deep into some antiquated C and C++ code (some here would say antediluvian ) I'd like to start instructing my team to clean up code as they go. We have ton of low level libraries that cannot be easily updated (time and budget concerns) And from what I can see, they work. Do you know of any documents and/or white papers that discuss this particular topic ? For now, I'm telling people to use
reference
instead of passing everything by pointers and useconst
as much as possible. To at least indicate code intent. I can't even use STL (string and vector) at this point. :-(CI/CD = Continuous Impediment/Continuous Despair
-
I have done this many times, doing it now for my current company. Old C code from the early 90s written to run on another OS (embedded) tweaked and tweaked into unmaintainability extreme. At the end of each phase the programming should produce exactly the same output/changes. My description here is for C, what I am working on now; it will work for any language(s). Phase 1 - find and deal with global variables. This is a judgement call as some things are properly global, some are just laziness on the part of some programmer. One tactic, make a structure, move them there to get them all organized. Refer to the globals only as part of the structure and pass only pointers to the structure. Control, you want to get control of the globals or at least make it clear where they are getting changed. In C/C++ a function that gets the structure read only will be "const", if it changes a value then not const, get it? Phase 2 - find duplicate (or near duplicate) code and create functions or subs to perform. Replace this code with calls. I call this "mining functions', you are digging them out of the code. Repeat till there are not more easy targets to mine. You can repeat this later in the process. Phase 3 - now look for unexecutable code - in large systems there will often be some. In the system I am working on now there were subs that never get called, functions that got called but the return values were ignored or tossed away. These really were changing global variables - that would be discovered in Phase 2, right? Phase 4 - look for bad algorithms and processes. for loops, while loops are the first targets. Repeat until you've done enough. Each phase makes it easier to see what to do int the next.
"some are just laziness on the part of some programmer" your post is excellent advice and I suspect you have scars :)
Charlie Gilley “They who can give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety.” BF, 1759 Has never been more appropriate.
-
Where I work that would be cost prohibitive, because of the amount of V,V & T involved.
~d~
-
I'm deep into some antiquated C and C++ code (some here would say antediluvian ) I'd like to start instructing my team to clean up code as they go. We have ton of low level libraries that cannot be easily updated (time and budget concerns) And from what I can see, they work. Do you know of any documents and/or white papers that discuss this particular topic ? For now, I'm telling people to use
reference
instead of passing everything by pointers and useconst
as much as possible. To at least indicate code intent. I can't even use STL (string and vector) at this point. :-(CI/CD = Continuous Impediment/Continuous Despair
Code does not rot. If it works it works. Only when it has or need to be interfaced with something new, does it need a rewrite. And by then he it may be better to write the function from scratch. Time is life. Are you willing to exchange life, yours or someone else, in order to change something that works for something that may or not work? That said in the rare occasions. Make sure you understand the requirements and don't go into change for the sake on change.Then change what needs to be changed.
-
Code does not rot. If it works it works. Only when it has or need to be interfaced with something new, does it need a rewrite. And by then he it may be better to write the function from scratch. Time is life. Are you willing to exchange life, yours or someone else, in order to change something that works for something that may or not work? That said in the rare occasions. Make sure you understand the requirements and don't go into change for the sake on change.Then change what needs to be changed.
Thanks. I don't think I want to change old code for the sake of change. I just want to make it possible to steer the team towards better/modern coding practice. Especially for new code or code that needs maintenance.
CI/CD = Continuous Impediment/Continuous Despair