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

Chapter 18

Networking-savvy readers may note that keeping a connection open to a host indefinitely is considered bad practice and doesn’t adhere to the HTTP specification. We’ve chosen elegance over etiquette in this example.

As you can see, the WebHost class provides an object-oriented wrapper around the C library. By providing an abstraction, you can change the underlying implementation without affecting client code or provide additional features, such as connection reference counting or parsing of pages.

Linking with C Code

In the previous example, we assumed that you had the raw C code to work with. The example took advantage of the fact that most C code will successfully compile with a C++ compiler. If you only have compiled C code, perhaps in the form of a library, you can still use it in your C++ program, but you need to take a few extra steps.

Compiled C code is in a different format from compiled C++ code, so you need to tell the compiler that certain functions are written in C so that the linker can properly make use of them. This is done with the extern keyword.

In the following code, the function prototype for doCFunction() is specified as an external C function.

extern “C” {

void doCFunction(int i);

}

int main(int argc, char** argv)

{

// Call the C function. doCFunction(8);

}

The actual definition for doCFunction() is provided in a compiled binary file that is attached in the link phase. The extern keyword above simply informs the compiler that the linked-in code was compiled in C.

A more common pattern for using extern is at the header level. For example, if you are using a graphics library written in C, it probably came with a .h file for you to use. You can write another header file that wraps the original one in an extern block to specify that the entire header defines functions written in C. The wrapper .h file is often named with .hpp to distinguish it from the C version of the header:

// graphicslib.hpp

extern “C” {

#include “graphicslib.h”

}

Whether you are including C code in your C++ program or linking against a compiled C library, remember that even though C++ is essentially a superset of C, they are different languages with different design goals. Adapting C code to work in C++ is quite common, but providing an object-oriented C++ wrapper around procedural C code is often much better.

498

Developing Cross-Platform and Cross-Language Applications

Mixing Java and C++ with JNI

Even though this is a C++ book, we won’t pretend that there aren’t newer and snazzier languages out there. The Java language took the programming world by storm in the mid-1990s and has grown immensely in popularity ever since. Java and C++ are similar languages to an extent, but they have different strengths. Without getting into a religious war, the most commonly cited advantage of C++ is its speed, and the most commonly cited advantages of Java are its built-in libraries for network programming and graphical interfaces.

The Java Native Interface, or JNI, is a part of the Java language that allows the programmer to access functionality that was not written in Java. Because Java is a cross-platform language, the original intent was to make it possible for Java programs to interact with the operating system. JNI also allows programmers to make use of libraries written in other languages, such as C++. Access to C++ libraries may be useful to a Java programmer who has a performance-critical piece of his application or who needs to use legacy code.

JNI can also be used to execute Java code within a C++ program, but such a use is far less common. There is currently much more legacy C++ code than legacy Java code, so most applications that use Java code are Java through-and-through. Because this is a C++ book, we do not include an introduction to the Java language. This section is targeted at readers who already know Java and wish to incorporate C++ code into their Java code.

To begin your cross-language adventure, start with the Java program. For this example, the simplest of Java programs will suffice:

public class HelloCpp {

public static void main(String[] args)

{

System.out.println(“Hello from Java!\n”);

}

}

The next step is a little strange. You need to declare a Java method that will be written in another language. To do this, you use the native keyword and leave out the implementation:

public class HelloCpp {

// This will be implemented in C++.

public native void callCpp();

public static void main(String[] args)

{

System.out.println(“Hello from Java!\n”);

}

}

C++ code will eventually be compiled into a shared library that gets dynamically loaded into the Java program. You need to load this library inside of a Java static block so that it is loaded when the Java program begins executing. The name of the library can be whatever you want this example uses hellocpp.so. A file ending in .so is a shared library on Unix systems. Windows users would most likely use a .dll file.

499

Chapter 18

public class HelloCpp {

static { System.load(“hellocpp.so”);

}

// This will be implemented in C++. public native void callCpp();

public static void main(String[] args)

{

System.out.println(“Hello from Java!\n”);

}

}

Finally, you need to actually call the C++ code from within the Java program. The callCpp() Java method serves as a placeholder for the not-yet-written C++ code. Because callCpp() is a method of the HelloCpp class, simply create a new HelloCpp object and call the callCpp() method.

public class HelloCpp {

static { System.load(“hellocpp.so”);

}

// This will be implemented in C++. public native void callCpp();

public static void main(String[] args)

{

System.out.println(“Hello from Java!\n”);

HelloCpp cppInterface = new HelloCpp();

cppInterface.callCpp();

}

}

That’s all for the Java side! Now, just compile the Java program as you normally would:

javac HelloCpp.java

Then use the javah program (the authors like to pronounce it jav-AHH!) to create a header file for the native method:

javah HelloCpp

After running javah, you will find a file named HelloCpp.h, which is a fully working (if somewhat ugly) C/C++ header file. Inside of that header file is a C function definition for a function called Java_HelloCpp_callCpp(). Your C++ program will need to implement this function to be called from within the Java program. The full signature is:

void Java_HelloCpp_callCpp(JNIEnv* env, jobject javaobj);

500

Developing Cross-Platform and Cross-Language Applications

Your C++ implementation of this function can make full use of the language. This example simply outputs some text from C++. First, you need to include the jni.h header file and the HelloCpp.h file that was created by javah. You will also need to include any C or C++ headers that you intend to use.

#include <jni.h> #include “HelloCpp.h” #include <iostream>

The C++ function is written as normal. Keep in mind that you are implementing a function, not writing a program. You will not need a main(). The parameters to the function allow interaction with the Java environment and the object that called the native code. They are beyond the scope of this example.

#include <jni.h> #include “HelloCpp.h” #include <iostream>

void Java_HelloCpp_callCpp(JNIEnv* env, jobject javaobj)

{

std::cout << “Hello from C++!” << std::endl;

}

Compiling this code depends on your environment, but you will most likely need to tweak your compiler’s settings to include the JNI headers and the location of the native Java library files. Using the gcc compiler on Linux, your compile command might look like this:

g++ -shared -I/usr/java/jdk/include/ -I/usr/java/jdk/include/linux HelloCpp.cpp \ -o hellocpp.so

The output from the compiler is the library that is used by the Java program. As long as the shared library is somewhere in the Java class path, you can execute the Java program normally:

java HelloCpp

You should see the following result:

Hello from Java!

Hello from C++!

Of course, this example just scratches the surface of what is possible through JNI. You can use JNI to interface with OS-specific features or hardware drivers. For complete coverage of JNI, you should consult a Java text.

Mixing C++ with Perl and Shell Scripts

C++ contains a built-in general-purpose mechanism to interface with other languages and environments. You’ve already used it many times, probably without paying it much attention — it’s the arguments to and return value from the main() function.

C and C++ were designed with command-line interfaces in mind. The main() function receives the arguments that a user types at the command line and returns a status code that can be interpreted by the caller. Many large graphical applications ignore the parameters to main() because graphical interfaces

501

Chapter 18

tend to avoid passing arguments. However, in a scripting environment, arguments to your program can be a powerful mechanism that allows you to interface with the environment.

Scripting versus Programming

Before delving into the details of mixing C++ and scripts, consider whether your project is an application or a script. The difference is subtle and subject to debate. The following descriptions are just guidelines. Many so-called scripts are just as sophisticated as full-blown applications. The question isn’t whether or not something can be done as a script, but whether or not a scripting language is the best tool.

An application is a program that performs a particular task. Modern applications typically involve some sort of user interaction. In other words, applications tend to be driven by the user, who directs the application to take certain actions. Applications often have multiple capabilities. For example, a user can use a photo editing application to scale an image, paint over an image, or print an image. Most of the software you would buy in a box is an application. Applications tend to be relatively large and often complex programs.

A script generally performs a single task, or a set of related tasks. You might have a script that automatically sorts your email, or backs up your important files. Scripts often run without user interaction, perhaps at a particular time each day or triggered by an event, such as the arrival of new mail. Scripts can be found at the OS level (such as a script that compresses files every night) or at the application level (such as a script that automates the process of shrinking and printing images). Automation is an important part of the definition of a script — scripts are usually written to codify a sequence of steps that a user would otherwise perform manually.

Now, consider the difference between a scripting language and a programming language. Not all scripts are necessarily written in scripting languages. You could write a script that sorts your email using the C programming language, or you could write an equivalent script using the Perl scripting language. Similarly, not all applications are written in programming languages. A suitably motivated coder could write a Web browser in Perl if she really wanted to. The line is blurry. In fact, the Perl language is so flexible that many programmers consider it both a programming language and a scripting language.

In the end, what matters most is which language provides the functionality you need. If you are going to be interacting with the operating system heavily, you might consider a scripting language because scripting languages tend to have better support for OS interaction. If your project is going to be larger in scope and involve heavy user interaction, a programming language will probably be easier in the long run.

A Practical Example — Encrypting Passwords

Assume that you have a system that writes everything a user sees and types to a file for auditing purposes. The file can be read only by the system administrator so that she can figure out who to blame if something goes wrong. An excerpt of such a file might look like this:

Login: bucky-bo

Password: feldspar

bucky-bo> mail

bucky-bo has no mail

bucky-bo> exit

502

Developing Cross-Platform and Cross-Language Applications

While the system administrator may want to keep a log of all user activity, she may wish to obscure everybody’s passwords in case the file is somehow obtained by a hacker. A script seems like the natural choice for this project because it should happen automatically, perhaps at the end of every day. There is, however, one piece of the project that might not be best suited for a scripting language. Encryption

libraries tend to exist mainly for high-level languages such as C and C++. Therefore, one possible implementation is to write a script that calls out to a C++ program to perform the encryption.

The following script uses the Perl language, though almost any scripting language could accomplish this task. We chose Perl because it is cross-platform and has facilities that make text parsing simple. If you don’t know Perl, you will still be able to follow along. The most important element of Perl syntax for this example is the ` character. The ` character instructs the Perl script to shell out to an external command. In this case, the script will shell out to a C++ program called encryptString.

The strategy for the script is to loop over every line of a file looking for lines that contain a password prompt. The script will write a new file, userlog.out, which contains the same text as the source file, except that all passwords are encrypted. The first step is to open the input file for reading and the output file for writing. Then, the script needs to loop over all the lines in the file. Each line in turn is placed in a variable called $line.

open (INPUT, “userlog.txt”) or die “Couldn’t open input file!”; open (OUTPUT, “>userlog.out”) or die “Couldn’t open output file!”;

while ($line = <INPUT>) {

Next, the current line is checked against a regular expression to see if this particular line contains the Password: prompt. If it does, Perl will store the password in the variable $1.

if ($line =~ m/^Password: (.*)/) {

Since a match has been found, the script calls the encryptString program with the detected password to obtain an encrypted version of it. The output of the program is stored in the $result variable, and the result status code from the program is stored in the variable $?. The script checks $? and quits immediately if there was a problem. If everything is okay, the password line is written to the output file with the encrypted password instead of the original one.

$result = `encryptString $1`; if ($? != 0) { exit(-1) }

print OUTPUT “Password: $result\n”;

If the current line was not a password prompt, the script simply writes the line as is to the output file. At the end of the loop, it closes both files and exits.

} else {

print OUTPUT “$line”;

}

}

close (INPUT); close (OUTPUT);

503