• Nem Talált Eredményt

Modular programs in C++

In document Mechatronic Systems Programming in C++ (Pldal 157-162)

II. fejezet - Modular programming in C++

3. Namespaces and storage classes

3.3. Modular programs in C++

If the descriptions of the functions of a C library are contained in a separated declaration file, it should be worth the following method:

extern "C" {

#include <rs232.h>

}

We should also use the extern "C" declaration if functions written and compiled in C++ are intended to be accessed from a C program code.

It is also worth mentioning here that the main () function also has extern "C" linkage.

3.3. Modular programs in C++

The structure of the example code of the preceding part becomes more structured if we create for the MatModule.cpp file a header file named MatModule.h that describes the interface of the module. Using constants becomes also simpler if they are placed in the file level scope (static). According to what has been said so far, the content of the MatModule.h header file:

// MatModule.h

#ifndef _MATMODULE_H_

#define _MATMODULE_H_

#include <cmath>

using namespace std;

// file level definitions/declarations const double pi = asin(1)*2;

const double e = exp(1);

static double Degree2Radian(double);

// program level declarations int Random(int n);

double Sin(double degree);

#endif

Lines with green and bold characters contain preprocessing statements that guarantee that the content of the MatModule.h header file would be included exactly once in the module containing the line

#include "MatModule.h"

(The 4. szakasz - Preprocessor directives of C++ provides readers with more information on the usage of preprocessors.)

Including the header file results in modifying the C++ modules:

// MainModule.cpp // MatModule.cpp

return sin(Degree2Radian(degree));

}

C++ compilers compile each module separately and create separate object modules for them because all the identifiers of each module have to be known in advance by compilers. This can be realized by placing declarations/prototypes and definitions adequately. After compilation, it is the task of the linker software to build one executable program from the object modules by resolving extern linkages from the object modules themselves or from standard libraries.

Integrated development environments automatize compilation and linking by creating projects. The only task of programmers is to add to the project the already existing C++ source files. In general, if we use the Build command from the menu, the whole compilation process takes place.

The GNU C++ compiler can be activated for compilation and for linking with the following command line command. It also makes the compiler create the Project.exe executable file:

g++ -o Project MainModul.cpp MatModul.cpp

3.4. Namespaces

Names (identifiers) used in the program are stored by C++ compilers in different places (on the basis of how they are used) that are called namespaces. Within a given namespace, stored names have to be unique but there may be identical names in different namespaces. Two identical names that are situated in the same scope but not in the same namespace designate different identifiers. C++ compilers differentiate between the following namespaces:

3.4.1. The default namespaces of C++ and the scope operator

In C language, variables and functions defined on the file level (as well as Standard library functions) can be found in the same common namespace. C++ enclose Standard library elements in the namespace called std whereas the other elements defined on a file level are contained within the global namespace.

In order to be able to refer the elements of a namespace, we have to know how to use the scope operator (::). We will talk more about the scope operator later in that book, so now we only show examples for two of its possible usages.

With the help of :: operator, we can refer names having file and program level scope (that is identifiers of the global namespace) from any block of a program.

#include <iostream>

We also use the scope operator to access directly names defined in the namespace std :

#include <iostream> namespaces having a user-defined name gives a good solution for these problems.

3.4.2.1. Creating namespaces

In source files, namespaces can be created either outside function blocks or in header files. In case a compiler find more namespaces with the same name, it unifies their contents, thus it extends the namespace defined earlier. To select a namespace, we have to use the keyword namespace:

namespace myOwnNamespace { definitions and declarations }

When planning program level namespaces, it is worth differentiating the namespaces containing definitions from those which can be placed in header files and which contain only declarations.

#include <iostream>

void nsexample::ReadData(int & a, std::string & s) {

std::cout << "name: "; getline(std::cin, s);

std::cout << "value: "; std::cin >> a;

}

If a function declared in a namespace is created outside that namespace, the name of the function has to be qualified by the name of its namespace (in our example nsexample::).

3.4.2.2. Accessing the identifiers of a namespace

The identifiers provided in the namespace called nsexample can be accessed from every module to which the version of that namespace containing the declarations is included. Identifiers of a namespace can be accessed in many ways.

Directly by using the scope operator:

int main() {

nsexample::name = "Sum";

nsexample::value = 123;

nsexample::PrintData();

nsexample::ReadData(nsexample::value, nsexample::name);

nsexample::PrintData();

std::cin.get();

}

Or by using the directive using namespace :

It is of course more comfortable to make accessible all names for the whole program with the directive using namespace:

using namespace nsexample;

int main() { name = "Sum";

value = 123;

PrintData();

ReadData(value, name);

PrintData();

std::cin.get();

}

Or by providing using -declarations:

With the help of using-declarations, we make available only the necessary elements from those of the namespace. This is the solution to choose if there is a name conflict between certain identifiers of a namespace.

int PrintData() {

std::cout << "Name conflict" << std::endl;

return 1;

}

int main() {

using nsexample::name;

using nsexample::value;

using nsexample::ReadData;

name = "Sum";

value = 123;

nsexample::PrintData();

ReadData(value, name);

PrintData();

std::cin.get();

}

3.4.2.3. Nested namespaces, namespace aliases

C++ makes it possible to create new namespaces within namespaces, that is to nest them in each other (nested

The elements of the namespace Project can be accessed in many ways:

using namespace Project::Intel;

using Project::Motorola::ToWord;

cout << hex; cout << Motorola::ToWord(0xab,0x12)<< endl; // ab12 }

// --- int main() {

cout<<hex;

cout<<Project::Intel::ToWord(0xab,0x12)<< endl; // 12ab cout<<Project::Motorola::ToWord(0xab,0x12)<<endl; // ab12 }

Accessing the elements of complex and multiple nested namespaces with long complex names (the elements of which are separated from each other by the namespace operator) makes program codes less legible. In order to solve this problem, C++ introduced the notion of namespace aliases:

namespace alias =externalNS::internalNS … ::most_internal_NS;

If we use aliases, the example above becomes much simpler:

int main() {

namespace ProcI = Project::Intel;

namespace ProcM = Project::Motorola;

cout << hex;

cout << ProcI::ToWord(0xab,0x12)<< endl; // 12ab cout << ProcM::ToWord(0xab,0x12)<< endl; // ab12 }

3.4.2.4. Anonymous namespaces

C++ standards do not support the usage of the keyword static either in file level scopes or in namespaces. In module level scopes, we can create variables and functions in anonymous namespaces that are not accessible from outside of the compilation unit. However, the identifiers of anonymous namespaces can be used without restrictions within a module.

Within the same module, many anonymous namespaces can be created:

#include <iostream>

#include <string>

using namespace std;

namespace {

string password;

}

namespace {

bool ChangePassword() { bool success = false;

string old, new1, new2;

cout << "Previous password: "; getline(cin, old);

if (password == old) {

cout << "New Password: "; getline(cin, new1);

cout << "New Password: "; getline(cin, new2);

if (new1==new2) { password = new1;

success = true;

} }

return success;

} // ChangePassword() }

int main() {

password = "qwerty";

if (ChangePassword())

cout << "Successful password change!" << endl;

else

cout << "Not a successful password change!" << endl;

}

In document Mechatronic Systems Programming in C++ (Pldal 157-162)