Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Professional C++ [eng].pdf
Скачиваний:
1715
Добавлен:
16.08.2013
Размер:
11.09 Mб
Скачать

Chapter 18

you get in a standard C++ program without platform extensions. For example, operations such as copy and paste are not directly supported in C++ and require platform-provided libraries.

Low-level files. In Chapter 14, you read about standard I/O in C++, including reading and writing files. Many operating systems provide their own file APIs, which are sometimes incompatible with the standard file classes in C++. These libraries often provide OS-specific file tools, such as a mechanism to get the home directory of the current user or access to OS configuration files. In general, once you start using the APIs for a particular platform, you should switch from C++ I/O classes to the platform’s I/O classes if any exist.

Threads. Concurrent threads of execution within a single program are not directly supported in C++. Their implementation depends heavily on the inner workings of the operating system, so threads were not included in the language. The most commonly used thread library is called pthreads. Many operating systems and object-oriented frameworks also provide their own threading models.

Cross-Language Development

For certain types of programs, C++ may not be the best tool for the job. For example, if your Unix program needs to interact closely with the shell environment, you may be better off writing a shell script than a C++ program. If your program performs heavy text parsing, you may decide that the Perl language is the way to go. Sometimes what you want is a language that blends the general features of C++ with the specialized features of another language. Fortunately, there are some techniques you can use to get the best of both worlds — the flexibility of C++ combined with the unique specialty of another language.

Mixing C and C++

As you already know, the C++ language is a superset of the C language. All C programs will compile and run in C++ with a few minor exceptions. These exceptions usually have to do with reserved words. In C, for example, the term class has no particular meaning. Thus, it could be used as a variable name, as in the following C program.

#include <stdio.h>

int main(int argc, char** argv)

{

int class = 1; // Compiles in C, not C++

printf(“class is %d\n”, class);

}

This program will compile and run in C, but will yield an error when compiled as C++ code. When you translate, or port, a program from C to C++, this is the type of error you will face. Fortunately, the fixes are usually quite simple. In this case, simply rename the class variable to classID and the code will compile.

The ease of incorporating C code in a C++ program comes in handy when you encounter a useful library or legacy code that was written in C. Functions and classes, as you’ve seen many times in this book, work just fine together. A class method can call a function, and a function can make use of objects.

494

Developing Cross-Platform and Cross-Language Applications

Shifting Paradigms

One of the dangers of mixing C and C++ is that your program may start to lose its object-oriented properties. For example, if your object-oriented Web browser is implemented with a procedural networking library, the program will be mixing these two paradigms. Given the importance and quantity of networking tasks in such an application, you might consider writing an object-oriented wrapper around the procedural library.

For example, imagine that you are writing a Web browser in C++, but you are using a C networking library that contains the functions declared in the following code. Note that the HostRecord and Connection data structures have been omitted for brevity.

// netwrklib.h

#include “hostrecord.h” #include “connection.h”

/**

*Gets the host record for a particular Internet host given

*its hostname (i.e. www.host.com)

*/

HostRecord* lookupHostByName(char* inHostName);

/**

* Connects to the given host */

Connection* connectToHost(HostRecord* inHost);

/**

* Retrieves a Web page from an already-opened connection */

char* retrieveWebPage(Connection* inConnection, char* page);

The netwrklib.h interface is fairly simple and straightforward. However, it is not object-oriented, and a C++ programmer who uses such a library is bound to feel icky, to use a technical term. This library isn’t organized into a cohesive class and it isn’t even const-correct! Of course, a talented C programmer could have written a better interface, but as the user of a library, you have to accept what you are given. Writing a wrapper is your opportunity to customize the interface.

Before we build an object-oriented wrapper for this library, take a look at how it might be used as is to gain an understanding of actual usage. In the following program, the netwrklib library is used to retrieve the Web page at www.wrox.com/index.html.

#include <iostream> #include “netwrklib.h”

using namespace std;

int main(int argc, char** argv)

{

HostRecord* myHostRecord = lookupHostByName(“www.wrox.com”); Connection* myConnection = connectToHost(myHostRecord);

495

Chapter 18

char* result = retrieveWebPage(myConnection, “/index.html”);

cout << “The result is “ << result << endl;

}

A possible way to make the library more object-oriented is to provide a single abstraction that recognizes the links between looking up a host, connecting to the host, and retrieving a Web page. A good objectoriented wrapper could hide the unnecessarily complexity of the HostRecord and Connection types.

Recalling the design principles you read about in Chapters 3 and 5, the new class should capture the common use case for the library. The previous example shows the most frequently used pattern — first a host is looked up, then a connection is established, then a page is retrieved. It is also likely that subsequent pages will be retrieved from the same host so a good design will accommodate that mode of use as well.

Following is the public portion of the definition for the WebHost class. This class makes the common case easy for the client programmer.

// WebHost.h

class WebHost {

public:

/**

* Constructs a WebHost object for the given host */

WebHost(const string& inHost);

/**

* Obtains the given page from this host */

string getPage(const string& inPage);

};

Consider the way a client programmer would use this class. To repeat the example used for the netwrklib library:

#include <iostream> #include “WebHost.h”

int main(int argc, char** argv)

{

WebHost myHost(“www.wrox.com”);

string result = myHost.getPage(“/index.html”);

cout << “The result is “ << result << endl;

}

The WebHost class effectively encapsulates the behavior of a host and provides useful functionality without unnecessary calls and data structures. The class even provides a useful new piece of functionality — once a WebHost is created, it can be used to obtain multiple Web pages, saving code and possibly making the program run faster.

496

Developing Cross-Platform and Cross-Language Applications

The implementation of the WebHost class makes extensive use of the netwrklib library without exposing any of its workings to the user. To enable this abstraction, the class needs a data member, as shown in the revised header file below.

// WebHost.h

#include “netwrklib.h”

class WebHost {

public:

/**

* Constructs a WebHost object for the given host */

WebHost(const string& inHost);

/**

* Obtains the given page from this host */

string getPage(const string& inPage);

protected:

Connection* mConnection;

};

The corresponding source file puts a new face on the functionality contained in the netwrklib library. First, the constructor builds a HostRecord for the specified host. Because the WebHost class deals with C++ strings instead of C-style strings, it uses the c_str() method on inHost to obtain a const char*, then performs a const cast to make up for netwrklib’s const-incorrectness. The resulting HostRecord is used to create a Connection, which is stored in the mConnection data member for later use.

WebHost::WebHost(const string& inHost)

{

const char* host = inHost.c_str();

HostRecord* theHost = lookupHostByName(const_cast<char*>(host));

mConnection = connectToHost(theHost);

}

Subsequent calls to getPage() pass the stored connection to netwrklib’s retrieveWebPage() function and return the value as a C++ string.

string getPage(const string& inPage)

{

const char* page = inPage.c_str();

string result = retrieveWebPage(mConnection, const_cast<char*>(page));

return result;

}

497