Click here to Skip to main content
15,881,248 members
Articles / Programming Languages / C++
Article

Writing Portable Code in C++

Rate me:
Please Sign up or sign in to vote.
3.29/5 (13 votes)
4 Dec 2003CPOL5 min read 51.1K   26   4
Writing Portable Code in C++

Introduction

Writing Portable code is interesting as well as a challenging task sometimes. You have to restrict yourself not only to the standard of the language but sometimes also the implementation of the language and its libraries on different platforms. And sometimes the standard of language doesn't say anything about the implementation of some features and lets the vendor to implement it in their own way. The most suitable example of this, which comes in my mind, is the sort algorithm of standard library. The C++ standard [ISO98] doesn't say anything which algorithm should be used for sorting, therefore vendors are free to use any sort function which fulfill the requirements of the standard.

Other problems which may come during the writing of portable code is different levels of compliance with standard of different compilers. Suppose you are working in an environment in which your team uses different compilers to compile project, then sometimes it becomes tricky, if not impossible, to write code which can run on all compilers. The ideal solution of this problem is to use the same compiler, which is as much compliant with standard as possible. But in real world it is not possible due to lots of factors such as personal preference, license issue, productivity, optimization etc. One example of such problem is the scope of variable in a loop.

According to C++ Standard the scope of variable which is declared inside the "for" loop is limited to the loop i.e. this code is legal according to C++ standard, but some compilers such as Visual C++ 6 can't compile it successfully.

const int iMax = 100;
for (int iIndex = 0; iIndex < iMax; ++iIndex)
{
  // do something
}

for (int iIndex = 0; iIndex < iMax; ++iIndex)
{
  // do something
}

And if we remove the declaration of variable inside the "for" loop then this code not only becomes illegal but also can't compile on standard C++ compilers like Comeau C/C++.

const int iMax = 10;
for (int iIndex = 0; iIndex < iMax; ++iIndex)
{
  // do something
}

for (iIndex = 0; iIndex < iMax; ++iIndex)
{
  // do something
}

The easiest solution of this problem is to put variable deceleration outside the scope of for loop to make all compilers happy.

const int iMax = 10;
int iIndex;

for (iIndex = 0; iIndex < iMax; ++iIndex)
{
  // do something
}

for (iIndex = 0; iIndex < iMax; ++iIndex)
{
  // do something
}

There is one problem with this technique and the problem is scope of iIndex variable. Now this variable become visible on that part of code too that it shouldn't. There is an interesting technique discussed in [DEV02] to limit the scope as well as make both compiler happy. The technique is to just put extra braces around the "for" loop to limit its scope visible inside the loop.

const int iMax = 10;
{for (int iIndex = 0; iIndex < iMax; ++iIndex)
{
  // do something
}}

{for (int iIndex = 0; iIndex < iMax; ++iIndex)
{
  // do something
}}

One more problem may arise during the writing of code that runs on multiple platform and this is the case sensitivity of file system. Windows preserve the case of file, but its file system is not case sensitive, however Unix file system is case sensitive. On Unix "Bounds.h" and "bounds.h" are two different files. So be careful to include files in proper case if you want to run it on different platforms.

Sometimes there may be interesting situations created due to different implementation of library, one such example is vector and string class in C++ Standard Template Library [JOS99]. Suppose you make a vector of 10 elements and shuffle it randomly using STL algorithm.

vector<int> vec;

for (int iIndex = 0; iIndex < 10; ++iIndex)
{
  vec.push_back(iIndex * iIndex);
}

random_shuffle(vec.begin(), vec.end());

After that due to any reason you want to sort range of element but not all elements. Suppose you do not want to sort first and last elements, it will be something like this

sort(vec.begin() + 1, vec.end() - 1);

Well what about writing the same code something like this

sort(++vec.begin(), --vec.end());

Isn't it equal to the previous line? Well the behavior of this code depends on the implementation of the standard library you use with your compiler. This code may work fine according to your requirement or it may give compilation error. To be more specific Visual C++ 7.0 compiles this code, on the other hand Visual C++ 6.0 will give compilation error.

To see why this code is not portable we have to take a look at specification of vector in C++ standard and its implementation in different libraries. According to C++ Standard vector does have random access iterator and store its elements in continuous memory location just like an array. The natural solution, which comes in mind to make iterator of vector pointer of template type because pointer holds all the specification of random access iterator. So the code to make vector class is something like this, here I use upper case letter to distinguish it from standard libraries names and omit unnecessary detail.

template <typename _Type>
class Allocator
{
public:
  typedef _Type* pointer;
  // other stuff
};

template <typename _Type, typename _Allocator = Allocator<_Type> >
class Vector
{
typedef typename _Allocator::pointer _Tptr;
  typedef _Tptr iterator;
  // other stuff

protected:
  iterator _First, _Last, _End;
};

And begin and end member function of vector just return the pointer of vector type.

template <typename _Type, typename _Allocator = Allocator<_Type> >
class Vector
{
  // other stuff
public:
  iterator begin()  { return (_First); }

  iterator end() { return (_Last); }

};

The ++ and -- operator on raw pointer return rvalue so you cant assign another value in it. This is the reason you can't use sort algorithm when random access iterator is implemented as a raw pointer. The code explains this operation in simplified way.

int arr[] = {1, 2, 3, 4, 5};

*(++arr) = 10;  // Can not compile
*(arr + 1) = 10;  // Compile

However some implementation wrap the pointer into a class. Here is simplest implementation of this in which extra detail is omitted.

template <typename _Type>
class Allocator
{
public:
  typedef _Type* pointer;
};
// wrap pointer into a class
template<class _Type>
class _Ptrit
{  
public:
  // other functions
  _Type& operator++()
  {  
    ++current;
    return (*this);
  }

  _Type operator++(int)
  {
    _Type _Tmp = *this;
    ++current;
    return (_Tmp);
  }

  _Type& operator--()
  {
    --current;
    return (*this);
  }
  
  _Type operator--(int)
  {
    _Type _Tmp = *this;
    --current;
    return (_Tmp);
  }

protected:
  _Type current;
};

template <typename _Type, typename _Allocator = Allocator<_Type> >
class Vector
{
  typedef _Allocator::pointer _Tptr;
  typedef _Ptrit<_Tptr> iterator;
  // other stuff

protected:
  iterator _First, _Last, _End;

public:
  // other functions
  iterator begin()
  {
    return (_First); 
  }

  iterator end()
  {
    return (_Last); 
  }
};

In this implementation the above code will compile. The same may arise in case of string because some implementation uses char* as a random access iterator.

string str("Hello World");

// may not compile on all implementations
sort(++str.begin(), --str.end());  

// compile
sort(vec.begin() + 1, vec.end() - 1); 

These are few reasons to make writing portable code challenging as well as fun. So be sure to check you code on different implementations on different platforms, if you want to make it portable, even if it is written according to language standard, because different implementations may behave differently on your code although they follow the rules of standard language.

Reference

  1. [DEW02] C++ Gotchas, Avoiding Common Problems in Coding and Design. Stephen Dewhurst 2002
  2. [ISO98] International Standard Programming Language C++ ISO/ICE 14882 1998
  3. [JOS99] The C++ Standard Library: A Tutorial and Reference. Nicolai M. Josuttis 1999

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader American Institute for Research
United States United States
Working as a Team leader in American Institute for Research

Comments and Discussions

 
Generalportable strings Pin
richardwest8-Dec-04 6:42
richardwest8-Dec-04 6:42 
GeneralTemporaries Pin
Arnt Witteveen12-Dec-03 23:55
Arnt Witteveen12-Dec-03 23:55 
QuestionVisual C++ 6 can't compile it successfully ??? Pin
N. Portable10-Dec-03 2:29
sussN. Portable10-Dec-03 2:29 
AnswerRe: Visual C++ 6 can't compile it successfully ??? Pin
Zeeshan Amjad10-Dec-03 17:50
Zeeshan Amjad10-Dec-03 17:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.