Usecases of diff when porting code
Today's post may seem redundant for most of you because most programmers already know what is diff and how to use it.
Diff spots the differences or equivalences between the contents of files. The comparison between two text files is mostly a solved problem, although diff can be defined for graphics, trees, lists or bags.
You don't often need to implement diff since the existing text-diff is usually enough for many tasks, but it's good thing to know that diff isn't a very precisely defined problem or limited to text files.
You can use diff as help when you are porting code to run on a new API while wanting to retain the old API in place. Here's a summary of the workflow with diff:
- Create a copy of the original project.
- Compile the program with the new API, replace code with stubs until it compiles.
- Run the program and iteratively improve the code until the program runs correctly under the new API.
- Run diff between the project and it's original copy, copy the differences over and adjust the project to run on both APIs.
- Check that the program runs on all APIs it has to run on.
If the differences are huge across APIs, this kind of workflow helps separating the concerns of getting the program to run and retaining the old API. The postponing of the later problem may help to think about the porting effort and lead to more effortless port, compared to attempting to immediately make it work on both APIs.
Stubbing the code
Stub is a placeholder for actual implementation. When porting it is usually a piece of code that fails when the program runs through that point.
#if defined(OLD_API)
int result = old_api_action();
api_specific_action(result);
#elif defined(NEW_API)
assert(false); // TODO: Not implemented, but fails on runtime
#else
#error Not implemented
#endif
In rare cases, when everything else you are aware about is using one way, and there's one stupid platform that is not behaving like the others. The following is acceptable:
#if defined(WIN32) // Fallen from the tree
HANDLE fh;
fh = CreateFile(thing, GENERIC_READ, 0, NULL, 0, FILE_ATTRIBUTE_NORMAL, NULL);
if (fh == INVALID_HANDLE_VALUE) fail();
#else // The proper POSIX way everyone else is using
int fd;
fd = open(thing, O_RDONLY, 0);
if (fd == -1) fail();
#endif
Here's yet another option you may want to consider:
#define Win32 1
#define Xcb 2
#if WINDOWING == Win32
int result = win32_action();
win32_specific_action(result);
#elif WINDOWING == Xcb
assert(false); // TODO: Not implemented, but fails on runtime
#else
#error Not implemented (WINDOWING)
#endif
Then you may use -DWINDOWING=Win32
to select it.
C compiler warns about redefined macros these days, so the both things above are fine ways to do it.
If the case is trivial then by all means implement it straight away. But keep in mind that any failure is always better than corruption.
When the above stubs are not sufficient for porting, you have the diff-method to help with the complex cases.
Good manners of porting code
Note how the stubs I provided always have an #else
case
that leads to #error
. This is intentional. Silent failures
are worst: do not create them with intent.
Diff in editors
Some editors provide views for comparing between the files.
For example, in Vim you have the command
:diffsplit
.
Here's an example of the command in the action for today's
blog post. The file to compare is the older revision of the
text:
Finally
Experimentation with your workflow should be quite safe thing to do. The methods that are inefficient are quickly discarded if they do not result in results.
Diff is a time-proven, good tool in the programmer's toolchain.