Writing Portable Code in C++’�A Vectors and String Issue

Environment: C++

Writing portable code is an interesting as well as a challenging task sometimes. You have to restrict yourself not only to the standard of the language but sometimes also to the implementation of the language and its libraries on different platforms. Sometimes the standard of language doesn’t say anything about the implementation of some features and lets the vendor implement it in their own way.

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

Sometimes, there may be interesting situations created due to different implementations of a library. One such example is vector and string classes in the C++ Standard Template Library [JOS99]. Suppose you make an vector of 10 elements and shuffle it randomly by using the 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, for any reason, you want to sort a range of elements, but not all elements. Suppose you do not want to sort the 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 a compilation error. To be more specific, Visual C++ 7.0 compiles this code; Visual C++ 6.0 will give you a compilation error.

To see why this code is not portable, we have to look at the specification of a vector in the C++ standard and its implementation in different libraries. According to the C++ standard, a vector does have a random access iterator and stores its elements in a continuous memory location, just like an array. The natural solution that comes to mind to make an iterator of vectors is a pointer of the template type. All the specifications for the random access iterator would be accessed with this pointer. So, the code to make a vector class is something like what follows. Here, I use upper-case letters 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;
};

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


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

iterator end() { return (_Last); }

};

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


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

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

However, some implementations wrap the pointer into a class. Here is the simplest implementation of this; again, 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 situation may arise in the case of a string because some implementations use 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 a few reasons why writing portable code is challenging as well as fun. So, be sure to check your code on different implementations on different platform if you want to make it portable, even if it is written according to a language standard, because different implementations may behave differently on your code although they follow the rules of a standard language.

See Writing Portable Code in C++—A Variable Scope Issue for more issues regarding the writing of portable code.

References


  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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read