{"id":8248,"date":"2024-10-26T03:00:00","date_gmt":"2024-10-26T07:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=8248"},"modified":"2024-10-19T14:42:05","modified_gmt":"2024-10-19T18:42:05","slug":"portable-programming-practices","status":"publish","type":"post","link":"https:\/\/www.both.org\/?p=8248","title":{"rendered":"Portable programming practices"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"8248\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">    <\/span>\r\n<\/div><\/div>\n<p>It would be great if every operating system acted exactly the same, so that we could write one program <em>one way<\/em>, and it would \u201cjust work\u201d <em>wherever we ran it<\/em>. But that\u2019s not how programming works \u2013 at least, not if your program needs to do anything that\u2019s nontrivial, such as using specific features of a user interface library.<\/p>\n\n\n\n<p>For small changes when programming across platforms, you can use <code>#if<\/code> to let the C preprocessor <a href=\"https:\/\/www.both.org\/?p=8051\">include different source code<\/a> to support different environments. But for writing portable programs that require larger changes, it might be easier to create <em>programming wrappers<\/em> that create an \u201cAPI\u201d or <em>Application Programming Interface<\/em> so that your program can use those API functions and effectively \u201chide\u201d the underlying platform-specific interfaces.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"define-the-interfaces\">Define the interfaces<\/h2>\n\n\n\n<p>Let\u2019s look at a simple example to see how you might create your own \u201cAPI\u201d to hide the system-specific programming interfaces. This is a variation on the classic \u201cHello world\u201d program that prints a string to the screen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;stdio.h&gt;\n\nint main()\n{\n    puts(\"Hello world\");\n    return 0;\n}<\/code><\/pre>\n\n\n\n<p>For this demonstration, let\u2019s create a <em>visual<\/em> version of the program that centers the text on the screen. When programming on Linux, we can use the <strong>ncurses<\/strong> library of functions. On FreeDOS, we can display text directly to the console using the <strong>conio<\/strong> and <strong>graph<\/strong> functions.<\/p>\n\n\n\n<p>First, we\u2019ll define a set of functions that we\u2019ll use as <em>interfaces<\/em> to control screen output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>extern int init_screen(void);\nextern int end_screen(void);\nextern int center_title(const char *s);\nextern int pause(void);<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>init_screen<\/code> will initialize the screen, and make it ready to print text to it<\/li>\n\n\n\n<li><code>end_screen<\/code> will reset the screen back to a normal mode, such as when we are done with the program and ready to quit<\/li>\n\n\n\n<li><code>center_title<\/code> will center a short string (text) to the screen, both vertically and horizontally<\/li>\n\n\n\n<li><code>pause<\/code> will wait for the user to press a key<\/li>\n<\/ul>\n\n\n\n<p>With these function definitions, we can write a short program to display the text \u201cHello world\u201d to the screen, then wait for the user to press any key and exit to the operating system:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;stdio.h&gt;\n\nextern int init_screen(void);\nextern int end_screen(void);\nextern int center_title(const char *s);\nextern int pause(void);\n\nint main()\n{\n    if (init_screen() == 0) {\n        puts(\"cannot initialize screen\");\n        return 1;\n    }\n\n    center_title(\"Hello world\");\n    pause();\n\n    end_screen();\n    return 0;\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-linux-functions\">The Linux functions<\/h3>\n\n\n\n<p>The Linux version of this program should run in text mode, using <a href=\"https:\/\/www.both.org\/?p=7500\">the ncurses library<\/a> to display text to the terminal. We can create a set of functions in a file called <code>screen.c<\/code> that provides <em>wrappers<\/em> to the ncurses functions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;curses.h>\n#include &lt;string.h>                    \/* strlen *\/\n\nint init_screen(void)\n{\n    initscr();\n    cbreak();\n    noecho();\n\n    return LINES;\n}\n\nint end_screen(void)\n{\n    if (endwin() == OK) {\n        return 1;                      \/* success *\/\n    }\n    else {\n        return 0;                     \/* fail *\/\n    }\n}\n\nint center_title(const char *s)\n{\n    int len;\n\n    len = strlen(s);\n    mvaddstr(LINES \/ 2, (COLS - len) \/ 2, s);\n\n    if (refresh() == OK) {\n        return len;                    \/* success *\/\n    }\n    else {\n        return 0;                      \/* fail *\/\n    }\n}<\/code><\/pre>\n\n\n\n<p>The functions isolate the extra code that we need to use the ncurses library. For example, the <code>init_screen<\/code> wrapper function calls <code>initscr<\/code> to initalize the screen using ncurses, then <code>cbreak<\/code> to disable line buffering and erase\/kill processing, and <code>noecho<\/code> to prevent functions like <code>getch<\/code> from printing the keystrokes to the screen. The <code>init_screen<\/code> wrapper returns the number of lines defined on the screen.<\/p>\n\n\n\n<p>This is a rather simple implementation of <code>init_screen<\/code>. A more robust version would detect if <code>initscr<\/code> failed, and exit early. But we\u2019ll use this version so it\u2019s easier to compare with the version we\u2019ll write later for FreeDOS, which also returns the number of lines on the screen.<\/p>\n\n\n\n<p>To manage input, we can define the <code>pause<\/code> function in a file called <code>input.c<\/code>. Splitting up the interfaces into separate files isn\u2019t strictly necessary, but it can make managing larger programs much easier because each file won\u2019t be too long.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;curses.h&gt;\n\nint pause(void)\n{\n    return getch();\n}<\/code><\/pre>\n\n\n\n<p>Because <code>input.c<\/code> and <code>screen.c<\/code> are both written to be specific to the Linux platform, you should save these in a separate directory called <code>linux<\/code>. Using a directory like this helps with project organization, and makes it easier to add similar support for other operating systems just by adding a new directory.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-freedos-functions\">The FreeDOS functions<\/h3>\n\n\n\n<p>We can create similar versions of these API wrappers that target the OpenWatcom C compiler on FreeDOS. Using OpenWatcom C, programs can access the console or <em>video mode<\/em> directly, which means the program can display text very quickly. The interfaces are different from the ncurses library on Linux, but these changes are not made visible to the <code>hello.c<\/code> program because they are hidden behind the wrapper functions. For example, the screen functions might be defined in a file called <code>screen.c<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;conio.h&gt;\n#include &lt;graph.h&gt;\n#include &lt;string.h&gt;                    \/* strlen *\/\n\nint init_screen(void)\n{\n    return _setvideomode(_TEXTC80);    \/* color 80x25 *\/\n}\n\nint end_screen(void)\n{\n    return _setvideomode(_DEFAULTMODE);\n}\n\nint center_title(const char *s)\n{\n    int len;\n\n    len = strlen(s);\n    _settextposition(25 \/ 2, (80 - len) \/ 2);\n    _outtext(s);\n\n    return (len);\n}<\/code><\/pre>\n\n\n\n<p>These wrapper functions don\u2019t need to be complex, but they include all of the platform-specific actions. For example, this version of <code>init_screen<\/code> is a one-line function that calls <code>_setvideomode<\/code> from OpenWatcom C. The <code>_setvideomode<\/code> puts the console into to requested mode (in this case, color text with 80 columns and 25 lines) and returns the number of lines in the new video mode, or zero if the mode failed.<\/p>\n\n\n\n<p>We can similarly define the <code>pause<\/code> function in a file called <code>input.c<\/code>, like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;conio.h&gt;\n\nint pause(void)\n{\n    int key;\n\n    key = getch();\n\n    if (key == 0) {\n        \/* extended key, call it again *\/\n        getch();\n    }\n\n    \/* returns 0 if extended key *\/\n    return key;\n}<\/code><\/pre>\n\n\n\n<p>The <code>getch<\/code> function in OpenWatcom is itself a kind of wrapper to the <code>INT 16,0<\/code> BIOS function to get a single key from the keyboard. The BIOS returns the scan code (between 0 and 255) of the key pressed by the user. If the key is an <em>extended<\/em> key such as <strong>F1<\/strong>, then the BIOS function returns zero, and you need to call it a second time to retrieve the scan code. That\u2019s why the <code>pause<\/code> function uses <code>getch<\/code> a second time if the first usage returned a zero value.<\/p>\n\n\n\n<p>Just as above, save both the <code>screen.c<\/code> and <code>input.c<\/code> functions in a directory for the platform. In this case, save them in a <code>dos<\/code> directory.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"compiling-for-different-systems\">Compiling for different systems<\/h2>\n\n\n\n<p>With these platform-specific functions isolated to different files, it\u2019s fairly straightforward to compile a separate version of the program for each platform: one for Linux, and one for FreeDOS.<\/p>\n\n\n\n<p>To compile on Linux, use this command line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ gcc -Wall -o hello hello.c linux\/*.c -lncurses<\/code><\/pre>\n\n\n\n<p>The <code>-Wall<\/code> option says to print all warnings; I often compile programs using this option so I can write warning-free code. For this example, the <code>-Wall<\/code> option is not necessary, but it\u2019s good to have.<\/p>\n\n\n\n<p>The <code>-lncurses<\/code> option at the end tells the compile to <em>link<\/em> with the <code>ncurses<\/code> library.<\/p>\n\n\n\n<p>Running the <code>hello<\/code> program displays the \u201cHello world\u201d text in the center of the terminal, and waits for the user to press a key:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"977\" height=\"677\" src=\"https:\/\/www.both.org\/wp-content\/uploads\/2024\/10\/portable_linux.png\" alt=\"Running the program on Linux\" class=\"wp-image-8249\" style=\"width:555px\"\/><\/figure>\n\n\n\n<p>To compile the program on FreeDOS, first copy the <code>hello.c<\/code> file and <code>dos<\/code> directory to a virtual machine running FreeDOS, then use OpenWatcom C to compile it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&gt; wcl -q hello.c dos\\*.c<\/code><\/pre>\n\n\n\n<p>The <code>-q<\/code> option tells the OpenWatcom C compiler to run \u201cquietly.\u201d This suppresses any information messages, and only prints warnings and errors. Without <code>-q<\/code>, OpenWatcom tends to prints a lot of extra information that we don\u2019t need here.<\/p>\n\n\n\n<p>This creates a <code>hello.exe<\/code> program that displays the text \u201cHello world\u201d in the center of the screen, and waits for the user to press any key:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"533\" height=\"400\" src=\"https:\/\/www.both.org\/wp-content\/uploads\/2024\/10\/portable_freedos.png\" alt=\"Running the program on FreeDOS\" class=\"wp-image-8250\" style=\"width:533px\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"writing-with-wrappers\">Writing with wrappers<\/h2>\n\n\n\n<p>Using wrapper functions like this can make it easier to write programs that support very different platforms. While <code>#if<\/code> can make small changes for portable programs, sometimes you need to make larger changes for different platforms, such as <em>screen display<\/em> or even a <em>graphical environment<\/em>. Isolating the platform-specific code in these wrapper functions helps to create more portable applications.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes it&#8217;s easier to define your API to hide the platform-specific code<\/p>\n","protected":false},"author":33,"featured_media":7679,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[340,5,150],"tags":[267,91,152],"class_list":["post-8248","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-freedos","category-linux","category-programming","tag-freedos","tag-linux","tag-programming"],"modified_by":"Jim Hall","_links":{"self":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/8248","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=8248"}],"version-history":[{"count":2,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/8248\/revisions"}],"predecessor-version":[{"id":8252,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/8248\/revisions\/8252"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/7679"}],"wp:attachment":[{"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=8248"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=8248"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=8248"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}