LinuxQuestions.org
Visit Jeremy's Blog.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 01-11-2011, 08:37 AM   #1
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443
Blog Entries: 3

Rep: Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723
How to write UI-independent code?


How do you write a program that is independent of the UI (the functionality is a library, and you have, for example, CLI and GUI interfaces as separate apps).

The problem I have is how to report status and prompt the user from within UI-independent code? I could split it into parts that do not require any user interaction, but then most of the functionality will be in the UI, not the backend.
 
Old 01-11-2011, 09:48 AM   #2
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
If it's a library, use callback functions. This is what e.g. PAM (pluggable authentication modules) do to enable conversation between any application and the PAM libraries.

If you already have a CLI application, you can write a GUI on top of it. The two can be separate processes, communicating via pipes (or named pipes or even UNIX domain sockets). If the GUI launches the CLI, normal pipes are the obvious choice; some popular media players such as mplayer and vlc support control this way.

If the application is a long-running service, and you want to have a GUI which attaches to and separates from the service cleanly, use named pipes or unix domain sockets for server-local communications, or perhaps UDP or TCP datagrams (locally via loopback) to allow a service with a GUI on a separate, networked machine. This is what e.g. MySQL does.

The real trick is to design the "conversation" so that it is easy to implement and to extend, while being simple and robust. One key point is to allow endpoints to parse (and ignore) unknown messages properly, without corrupting the conversation; this way future extensions are backwards compatible. With a library, you'd most likely use standardized binary data structures, but with other methods, text-based messaging is probably better - even with the increase in message size and the slight processing overhead. (With large amounts of binary data, tagged binary formats with a specific endianness is also a viable choice.)

Without further details, that's about as specific as I can get. Hope this helps,
Nominal Animal

Last edited by Nominal Animal; 03-21-2011 at 01:55 AM.
 
Old 01-11-2011, 11:06 AM   #3
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443

Original Poster
Blog Entries: 3

Rep: Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723
Quote:
Originally Posted by Nominal Animal View Post
If it's a library, use callback functions. This is what e.g. PAM (pluggable authentication modules) do to enable conversation between any application and the PAM libraries.
That's a nice idea, but to be practical for something complex it seems like you have to use a language with strong support for closures and anonymous, first-class functions.

For example, we have a function that removes a list of files and has a parameter that takes a function to call for each failed file:

Code:
remove_files(list_of_files, func(file)
    # create warning dialog, append to status dialog, or print message in terminal
endfunc)
Seems like this would be complex and difficult to manage if functions can only have fixed, global names and closures were not supported.

Last edited by MTK358; 01-11-2011 at 11:10 AM.
 
Old 01-11-2011, 04:16 PM   #4
paulsm4
LQ Guru
 
Registered: Mar 2004
Distribution: SusE 8.2
Posts: 5,863
Blog Entries: 1

Rep: Reputation: Disabled
Hi -
Quote:
If it's a library, use callback functions.
Absolutely correct. In fact, you don't even have to qualify with "if it's a library".
Quote:
That's a nice idea, but to be practical for something complex it seems like you have to use a language with strong support for closures and anonymous, first-class functions.
Horse hocky That's absurd, and untrue
Quote:
If you already have a CLI application, you can write a GUI on top of it.
Also a good idea, if the command-line program is not interactive. For example, "gcc" or "svn" would be excellent candidates; "vi" not so much

Here are two good Wikipedia articles that might be of interest:

http://en.wikipedia.org/wiki/Signals_and_slots

http://en.wikipedia.org/wiki/Callbac...ter_science%29
 
Old 01-11-2011, 04:22 PM   #5
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443

Original Poster
Blog Entries: 3

Rep: Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723
Quote:
Originally Posted by paulsm4 View Post
Horse hocky That's absurd, and untrue
How?

What if you've got a complex function with a lot of state variables that are needed for handling callbacks from the library?

Show me an easy way to do it other than closures.
 
Old 01-11-2011, 05:53 PM   #6
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
I think paulsm4 brings up a significant point. A text-mode application that is just a run-and-done application that takes input from the commandline and/or environment and/or config files is a whole different animal from one that runs and interacts with a user. GUIs that are wrappers around the former type of application are somewhat common. For example, almost all of the GUI CD/DVD burner applications are simply wrapper code around the underlying cdrecord family of tools.
On the other hand, are applications that are interactive. These tend to have specific ideas about what resources they use to interact with users, and are probably not so easy to put wrappers around (some of the gdb GUIs would fit this class of application).

I would like the OP to provide some clarification on the question. The value of separating the base functionality of the code from the UI functionality seems clear. Is the question really about how to build such an architecture, or is the question about how to craft code which can be ported to differing flavors of UI support? In large part, I think the specific UI system will impose a structure to which your code will have to conform. Very often, this will take the form of event-driven handlers that you can code to respond to UI events such as keystrokes and pointer events. These are invariably UI-specific (eg, GTK, Motif, Qt, Windows, Tk, etc.). While they mostly tend to follow the same patterns, I doubt that it is practical to build in UI independence into an application. Some UI packages/libraries use a significantly different API pattern, such as curses.

--- rod.
 
Old 01-11-2011, 06:21 PM   #7
dugan
LQ Guru
 
Registered: Nov 2003
Location: Canada
Distribution: distro hopper
Posts: 11,225

Rep: Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320
Quote:
Originally Posted by MTK358 View Post
What if you've got a complex function with a lot of state variables that are needed for handling callbacks from the library?

Show me an easy way to do it other than closures.
Make the function a method of a class, and put the state variables in the class?
 
Old 01-11-2011, 06:40 PM   #8
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by MTK358 View Post
What if you've got a complex function with a lot of state variables that are needed for handling callbacks from the library?
In C, it's the original callers responsibility to construct the state needed by the callback function prior to calling the library function, and to cleanup it after the library function exits. If the callback function may interrupt or cancel the operation, it is the responsibility of your callback function and your application to agree who does the cleanup then.

The library just does not care a whit.

An example library function with callback support in C:
Code:
struct file_information {
    mode_t              mode;
    uid_t               user;
    gid_t               group;
    off_t               st_size;
    char const *const   filename;
    char const *const   fullpath;
};

int multi_file_remove(const char *const pattern, int (*callback)(struct file_information *, void *), void *payload)
{
    struct file_information *info;
    int result;

    /* ... A lot of setup work ... */

    while (...) {

        /* Construct the description of a file into info */

        if (callback) {
            result = callback(info, payload);
            if (result) {
                /* Callback function aborted the operation! */
                /* ... Cleanup ... */
                return result;
            }
        }
    }

    /* ... Some cleanup work ... */

    return 0;
}
The interface is described by a header file that might look like
Code:
#ifndef   SOMELIBRARY_H
#define   SOMELIBRARY_H
#include <unistd.h>
#include <sys/stat.h>

struct file_information {
    mode_t              mode;
    uid_t               user;
    gid_t               group;
    off_t               st_size;
    char const *const   filename;
    char const *const   fullpath;
};

int multi_file_remove(const char *const, int (*)(struct file_information *, void *), void *);

#endif /* SOMELIBRARY_H */
If you don't need the callback functionality, your application can be as simple as
Code:
#include <stdio.h>
#inclide <somelibrary.h>

int main(void)
{
    int result;
    result = multi_file_remove("/*/*/*", NULL, NULL);
    return (result) ? 1 : 0;
}
A version of the above which outputs each file as it's being deleted via a callback:
Code:
#include <stdio.h>
#inclide <somelibrary.h>

int my_progress_meter(struct file_information *info, void *output)
{
    fprintf((FILE *)output, "Deleted file '%s'\n", info->fullpath);
    return 0;
}

int main(void)
{
    int result;
    result = multi_file_remove("/*/*/*", my_progress_meter, (void *)stdout);
    if (result) {
        fprintf(stderr, "Multi file remove failed.\n");
        return 1;
    } else {
        fprintf(stderr, "Multi file remove succeeded.\n");
        return 0;
    }
}
If nothing else, please see how the C library function qsort is implemented: your own program must supply a function that does the actual comparison. The library function just does the memory copies and comparisons in the most efficient order it can. You can compare structures literally megabytes in size, as complex as you ever wish. And it's a C89 function, practically ancient.

In a GUI application, the callbacks are typically asynchronous triggers or updates to fields or variables. And since most GUI APIs are easiest in C++ (or perhaps Python, take your pick), you can follow dugans advise and use the OOP features for even easier implementation. It has been done, and it's not really difficult.
Nominal Animal

Last edited by Nominal Animal; 03-21-2011 at 02:36 AM. Reason: Post deleted as requested by Xav
 
Old 01-11-2011, 07:18 PM   #9
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443

Original Poster
Blog Entries: 3

Rep: Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723
I kind of get it now.

To answer theNbomr's question, my question is how to make a highly interactive backend that would make it easy to write frontends such as plain command line, ncurses, GTK+, QtGui, etc.
 
Old 01-12-2011, 03:00 AM   #10
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Just wanted to point out that VLC seems to have already solved lot of these problems. It does have a surprising variety of "frontends" and interactive control methods. It might be worth your while to check out the sources.

Last edited by Nominal Animal; 03-21-2011 at 02:07 AM.
 
Old 01-12-2011, 10:35 AM   #11
dugan
LQ Guru
 
Registered: Nov 2003
Location: Canada
Distribution: distro hopper
Posts: 11,225

Rep: Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320Reputation: 5320
MPD would be another example.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
how to write code with the better quality lordofring Programming 13 10-30-2005 01:48 AM
Diffrerence between position independent code and Relocatable code? eshwar_ind Programming 7 05-11-2004 01:40 AM
Write to code segment Earp Programming 4 04-22-2004 01:49 PM
write a caracter from it's ASCII code xStef Linux - General 2 08-18-2003 09:27 AM
i can't write C++ code in glade amr_azima Programming 1 03-21-2002 02:43 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 07:57 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration