General Library Information - C++

This chapter contains general information applicable to all of the C++ programming libraries of XML-RPC For C/C++ (libxmlrpc++, libxmlrpc_server++, etc).

You will also find information of general applicability in the manual for libxmlrpc++, because it includes basic classes that you use with all the other libraries.

The equivalent chapter for the C libraries is General Library Information - C.

You must know something about XML-RPC (the protocol) to understand this chapter. You don't have to know the details of the protocol, since Xmlrpc-c is meant to spare you from learning that, but you do have to know the kinds of things that make up an XML-RPC transaction.

Almost everything you need to know about XML-RPC is here. But you will find that the official specification of the protocol is riddled with ambiguity and other usability problems. This page adds to it and accurately describes most so-called "XML-RPC" clients and servers.

Chapter Contents

C++ Namespace

Everything specific to Xmlrpc-c is in the C++ namespace xmlrpc_c. For example, the name of the XML-RPC value class is xmlrpc_c::value.

There are a few utilities that are packaged with Xmlrpc-c, but really are not specific to Xmlrpc-c's main purpose (they would be useful in many other arenas). These are in other namespaces. For example, Xmlrpc-c contains a class for errors. An object of this class is not specific to Xmlrpc-c in any way -- any program that generates errors could use it. It is in the girerr namespace (don't ask what the name means — it's essentially arbitrary).

Success/Failure

A method either succeeds or fails. The distinction might be a little fuzzy sometimes, but the documentation should make it clear what is considered successful.

When a method fails, it throws an object. Pointed-to or referenced return variables have arbitrary values when the function returns.

A method does not change the state of anything when it fails. For all practical purposes, it is the same as if the method had not been called.

While the object a method throws when it fails does contain information specifying how it failed, you generally aren't supposed to call a method just to see how it fails and your program should not analyze the failure information any further than to discern the type of object it is. The failure information within the object is primarily intended for forwarding to a human to diagnose a larger problem. There generally exist interfaces whereby you can learn from a successful execution whatever you would have learned from some failed execution.

A method never throws an object (i.e. it always completes and returns) when it succeeds.

The method an object throws when it fails is usually a girerr::error, described under Error Object. But if lower level libraries or code you supply throws something else up to an Xmlrpc-c method, the Xmlrpc-c method often just throws that on up.

Memory Management

An Xmlrpc-c method never destroys an object you created, or expects you to destroy an object it created. When an Xmlrpc-c method creates an object and returns it to you, it does so with a shared pointer created by you such that the object gets deleted automatically after you've destroyed your pointers.

Likewise, Xmlrpc-c never retains a reference/pointer to an object you created so that you have to worry about when you can safely destroy it. (Of course, when you pass an object as a parameter to a method, you cannot destroy the object while the method is running).

Naming Conventions

Names follow the form "paramCount". They don't use underscores to separate words. Names use underscores in some cases to reflect inheritance hierarchy, such as in "value_int" (which is derived from "value"). And sometimes underscores reflect other naming hierarchies.

C++ Named Arguments

The Xmlrpc-c libraries use a common C++ use paradigm that allows a method to have named arguments, which is not possible in C++ itself. Named arguments means the caller can specify arguments in any order and omit any of them. It also means the call is easy to read. And future versions of the method can have additional possible arguments without any loss of backward compatibility.

For our explanation, we will use the following example: The connectIt method creates an HTTP connection. You must tell it either a host name or IP address and optionally TCP port number of the server and name of the local network interface to use. The following are possible calls, all of the same method:


    connectIt(connectOpt()
              .hostname("www.google.com")
              .port(8080)
             )

    connectIt(connectOpt()
              .hostaddr("127.0.0.1")
              .interface("eth0")
             )

    connectIt(connectOpt()
              .interface("eth0")
              .hostaddr("127.0.0.1")
              .port(8080)
             )


To the unaccustomed, these examples don't even look like legal C++, but in fact it is quite basic C++. The periods on the left are just the "member" operator, as in mystring.length(). The code just isn't formatted in the typical way.

Here's how it works:

The one argument to the constructor is a connectOpt object. That option contains all of your option specifications. In the first example, it knows that you specified a host name and port number (and the values you specified), but not an IP address or interface name. connectOpt's constructor takes no arguments and generates an object that says you specified no arguments.

A connectOpt has a "setter" method to set each of the arguments. So connectOpt().hostname("www.google.com") generates a connectOpt that says you specified the host name.

But here is the key to the cleverness: The setter method's return value is a reference to the very object of which it is part. So you can use the above expression as the argument to the connectIt method. Or you can use it to call another setter method, in a boundless chain: opt().arg1Setter(arg1).arg2Setter(arg2)... That's what you see in the examples above, but formatted for easier reading.

Here are definitions that make the examples work:


    class connectOpt {
    public:
        connectOpt() {
            present.hostname  = false;
            present.hostaddr  = false;
            present.port      = false;
            present.interface = false;
        }

        connectOpt & hostname(string const& arg) {
            present.hostname = true;
            value.hostname   = arg;
        }
        connectOpt & hostaddr(string const& arg) {
            present.hostaddr = true;
            value.hostaddr   = arg;
        }
        connectOpt & port(string const& arg) {
            present.port = true;
            value.port   = arg;
        }
        connectOpt & interface(string const& arg) {
            present.interface = true;
            value.interface   = arg;
        }

        struct {
            string       hostname;
            string       hostaddr;
            unsigned int port;
            string       interface;
        } value;

        struct {
            bool hostname;
            bool hostaddr;
            bool port;
            bool interface;
        } present;
    };
    
    connectIt(connextOpt const& opt);

Thread Safety

The libraries, and objects of the classes implemented by them, are thread-safe. The libraries do not use static variables.

As an exception, some libxmlrpc_client++ objects are not thread safe.

The libraries' thread safety is of course depends upon thread-safety of all the libraries it uses. Your standard C library in particular must be thread-safe. As far as I know, all libraries that Xmlrpc-c uses in typical configurations are thread-safe.

We use the same definition of thread-safety as with the C libraries.