• No se han encontrado resultados

La ecografía como herramienta diagnóstica y terapéutica en Fisioterapia

Let us work through a detailed example of how to create a multi-file project.

Begin by creating a new C++ project called MyLib. This is to distinguish it from the model code on the website FMLib. When this section is complete, the projects should in fact be identical.

Our aim is to write a small library that makes available a normcdf function and a norminv function that compute the cumulative distribution function of the normal distribution and its inverse. We also want to make available a standard constant PI.

In a separate file, we want to write a small main function to show how the library can be used.

5.3.1 Creating the first header file

So that other files can use these functions, we will need to declare them in a header file. This is a C++ file with the file name extension .h.

You should now create a header file called matlib.h following the instruc-tions for you development environment below:

• If you are using Visual Studio to create your C++ projects, you should create all .h files by right-clicking on the folder Header Files.

• If you are using Unix, simply create header files in the same folder as your .cpp files but give them the extension .h.

• If you are using XCode, press CMD + N and select header file.

Now edit the file you have just created and type in the first line:

#pragma␣once

Then enter the declarations for all the functions we want to make available from our library. In our example, the complete code required is:

# p r a g m a␣o n c e

c o n s td o u b l e␣PI␣=␣3 . 1 4 1 5 9 2 6 5 3 5 8 9 7 9 ; /* *

*␣ C o m p u t e s␣the␣c u m u l a t i v e

*␣ d i s t r i b u t i o n␣f u n c t i o n␣of␣the

*␣ n o r m a l␣d i s t r i b u t i o n

*/

d o u b l e␣n o r m c d f (␣d o u b l e␣x␣);

/* *

*␣ C o m p u t e s␣the␣i n v e r s e␣of␣n o r m c d f

*/

d o u b l e␣n o r m i n v (␣d o u b l e␣x␣);

This contains the declarations for all the functions we wish to make avail-able. It contains both the declaration and the definition for PI.

In general, you should put the following in your header file:

(i) The first line #pragma␣once. We’ll explain why shortly.

(ii) Declarations of functions you wish to make available from your library.

(iii) Declarations of global variables you wish to make available.

(iv) Definitions of constant global variables.

Whereas in your source file you should put:

(i) Definitions of functions declared in the header.

(ii) Definitions of global variables missing from the header.

We will expand on these rules throughout this book as we meet more types of declaration and definition.

The reason why constants are treated differently is that the C++ compiler can “inline” them. This means to replace every use of the constant with the actual value. This gives a slight performance boost. As a consequence, how-ever, the definition needs to be in the header, so that every file knows the correct value to inline.

5.3.2 Some code that uses the functions

In a source file called main.cpp write the following code

# i n c l u d e␣< i o s t r e a m >

# i n c l u d e␣" m a t l i b . h "

u s i n gn a m e s p a c e␣std ; int␣m a i n ()␣{

c o u t␣< <␣" n o r m c d f ( 1 . 9 6 ) = "

< <␣n o r m c d f ( 1 . 9 6 )␣< <␣" \ n " ; c o u t␣< <␣" n o r m i n v ( 0 . 9 7 5 ) = "

< <␣n o r m i n v ( 0 . 9 7 5 )␣< <␣" \ n " ; r e t u r n␣0;

}

Note that we use #include to load in all the definitions in libraries. We use angle brackets when loading in standard libraries and quotation marks for our own libraries.

The reason for this is that our own libraries keep changing all the time, whereas the standard libraries won’t. By using angle brackets we are telling the compiler that a given file won’t have changed since the last compilation.

This allows the compiler to run a bit faster.

At this point, you should attempt to build the code, but you should expect it to fail. After all, we haven’t provided any function definitions yet.

Try to build the code to see what error message to expect when we for-get to write the definition for a function—or indeed what happens when the definition and the declaration don’t match precisely.

When I tried to run this code on Visual Studio, I got this error:

main.obj : error LNK2019:

unresolved external symbol "double __cdecl norminv(double)" (?norminv@@YANN@Z) referenced in function _main

This is called a linker error. The phrase “unresolved external” is an unhelpful way of saying that either:

(i) you forgot the definition altogether;

(ii) the type information in the definition doesn’t exactly match the type information in the declaration;

(iii) you haven’t installed a library correctly;

(iv) some more subtle problem has happened. For example, on Visual Studio you need to check whether the cpp file is actually listed under "Source Files" in your project.

If you examine the error message carefully, you can see that the name of the function (norminv) can be found hidden in the text. You just have to be willing to ignore all the parts of the error message you don’t understand. It is worth trying to remember what this error message looks like in your development environment so that when a similar linker error occurs you will be able to figure out what the problem is.

The process of turning C++ source files into executables is called “build-ing” the code. This is a three-stage process.

(1) The pre-processor performs simple text manipulation on the cpp files such as #include statements. The statement #include for example just means read in a copy of another file so we can use all the declarations from that file.

(2) The resulting cpp files are compiled.

(3) The compiled versions of all the files and all the libraries are linked to-gether. Each use of a function is linked to the place where it is defined.

Function declarations are used during the compile phase and all the function definitions are compiled. However, there is no check that everything glues together coherently until the linker stage. This is why mismatches between declarations and definitions are found at the linker stage.

5.3.3 Write the definitions

Create a file called matlib.cpp to contain the function definitions. Start the file with the line #include␣matlib.h.

In general you should have a clear correspondence between your header files and your source files. The exceptions are: It is conventional on Windows to have a file called stdafx.h which just contains #include statements for the libraries you want to use; you should have a main.cpp for testing. We will follow these conventions on all operating systems, not just Windows.

Whenever you write a .h file, make sure that the first line of the corre-sponding .cpp file includes the .h file. This is an important test that your .h file is correct and includes everything that it needs.

In the rest of the file, you should write the necessary code for the function definitions. If you succeeded in writing these in the exercises, you can use your own code. If not, it might be quicker to use the code below.

double␣norminv(␣double␣x␣)␣{

␣␣␣␣return␣1234.0;␣//␣TODO␣fix␣this }

double␣normcdf(␣double␣x␣)␣{

␣␣␣␣return␣1234.0;␣//␣TODO␣fix␣this }

You should now find that everything builds correctly.

As an experiment, make a change to the type in one of the function defi-nitions as follows.

//␣change␣double␣to␣float double␣norminv(␣float␣x␣)␣{

␣␣␣␣return␣1234.0;␣//␣TODO␣fix␣this }

You should find that you get a linker error, which you can easily fix by revert-ing to the original code.

5.4 How header files work