Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Some helpful tools

4.74/5 (16 votes)
15 May 2014CPOL8 min read 15.2K   176  
Lightweight profiling and test units; template for operator = overloading.

Introduction

Hi! I want to offer to you tree tools, which help me in my everyday work (especially first two). Of course, they are maybe successfully used a years before this article, but I’m not claiming to be original author of these tools. I simply want to share them, as I do it for my students. So, let’s begin.

A first one: A cross platform execution measurement tool

If you will say after reading this part, that “this is a bicycle” I will agree. But I like my bicycles! They are simple, convenient, are fast to write them and there is no need to study some big tool, intended to cover everything, if I want simply to compare two algorithms to deduce: which are faster. Working under Windows I’m used to QueryPerformanceCounter api function, which “retrieves the current value of the high-resolution performance counter” (citation from MSDN) . But work with cross platform applications has required more versatile tool. Here it is:

#include <stdint.h>

//this tool is compatible for x86_64 architecture only
#ifdef _WIN32                //  Windows
   #include <intrin.h>

   uint64_t rdtsc()
   {
       return __rdtsc();
   }
#else                        //  Linux/GCC
   uint64_t rdtsc()
   {
      unsigned int lo,hi;
      __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
      return ((uint64_t)hi << 32) | lo;
   }
#endif //_WIN32 

#define EXPR_TIME_CONS_AVER(nCount, expr, bShowCurTime)\
{\
   char ExprText[sizeof(#expr) + 1] = {0};\
   memcpy(ExprText, #expr, sizeof(#expr));\
   if(bShowCurTime == true)\
     cout<<"=== "<<ExprText<<" === start"<<endl;\
   uint64_t ui1, ui2, uiTicks, uiAverage = 0;\
   for(int iIn = 0; iIn < nCount; ++iIn)\
   {\
      ui1 = rdtsc();\
      expr;\
      ui2 = rdtsc();\
      uiTicks = ui2 - ui1;\
      uiAverage += uiTicks;\
      if(bShowCurTime == true)\
         cout<<uiTicks<<endl;\
   }\
   cout<<"=== "<<ExprText<<" average == "<<uiAverage / nCount<<"\n\n";\
}

This is a macro. Despite the fact, that we can’t debug them, they still have more pluses (de facto, we can debug macroses simply by put previously their code in a function).

  1. We can pass to it entire instruction. Not as a parameter in a function, which must have some type, but pass any instruction or even several of them.
  2. # operator can save any text (don’t forget: any code is just a plain text), and gives us ability to work with passed instruction as with conventional const char *.

Let me explain every tricky lines of code:

rdtsc(); - envelop function that returns a number of CPU clock’s ticks.
#define EXPR_TIME_CONS_AVER(nCount, expr, bShowCurTime)

It is a definition of the macros, which intended to show in console an average execution time. First param nCount – how many times a passed instruction must be executed to calculate an average execution time (here in the cycle for(int i;… ). Second expr – instruction, that will be executed in the cycle. Third bShowCurTime – is responsible for showing in console window a current execution time for every iteration. It is helpful if we want to see approximately pure execution time, because if during execution of our instruction another thread will interfere and switch context, time will be drastically bigger. (You can add in this macros additional parameters – array of uint64_t and it’s size to process outside the times and exclude those values, that are definitely out of sequence.)

char ExprText[sizeof(#expr) + 1] = {0};
Here we have the following: #expr – treats passed instruction as a const char *. sizeof(#expr) + 1 – operator sizeof can calculate at compile time a size of char array (our instruction), so we can declare another char array ExprText (to be a buffer) on the stack. +1 needed to put ‘\0’ at the it’s end = {0}; . (to output it using convenient c/c++ tools) char ExprText – I included this array in code simply as a hint: how you can return this string out of macros to work with it in any way you like. You can use # operator in a macros only. For example:
  1. Let’s compare per element copying of int array in a “for” cycle with work of memcpy function.
  2. What is faster? Copy constructor with 9 lines in disassembled code or move constructor with 16 lines. (in gcc you need to add c++11 support to compiler arguments) In Array.cpp
Array::Array(const Array & Right): m_nSize(Right.m_nSize),
       m_nReadonly(Right.m_nReadonly)
{
   m_pAr = new int[m_nSize];
   memcpy(m_pAr, Right.m_pAr, sizeof(int) * m_nSize);

}

Array::Array(Array && Right) noexcept : m_nSize(Right.m_nSize),
       m_pAr(Right.m_pAr), m_nReadonly(Right.m_nReadonly)
{  
     Right.m_nSize = 0;
     Right.m_pAr = 0;

}

In main:

#define ARR_SIZE 2000
int iAr[ARR_SIZE], iAr2[ARR_SIZE];

for(int i = 0; i < ARR_SIZE; ++i)
   iAr[i] = rand();

int nSamplesCount = 3;
EXPR_TIME_CONS_AVER(nSamplesCount,
for(int j = 0; j < ARR_SIZE; ++j) iAr2[j] = iAr[j], true);

EXPR_TIME_CONS_AVER(nSamplesCount,
memcpy(iAr2, iAr, sizeof(int) * ARR_SIZE),  true);

cout<<endl;

Array ar(10);
EXPR_TIME_CONS_AVER(nSamplesCount, Array ar2(ar), false);
EXPR_TIME_CONS_AVER(nSamplesCount, Array ar3(std::move(ar)), false);

Output (gcc 4.8.0):

Image 1

Try it, and you will discover a lot of interesting and unexpected, particularly working with different compilers (trust me!).

Second tool: Using preprocessor to write a simple test units.

I have started to use this trick since I was a freelancer. It is simply a convenient feature of preprocessor , which I use to quickly turn ON or OFF a testing code in my (not very big) projects. Now I use it also to accustom my students to check all questionable data together with asserts, exception handling or writing in file. Boost.test and CppUnit are really good, but they required a lot of time to master it. Here is the much simpler variant. Look:

We have two ways to declare our own directive: as a macros:

#define Test_FuncTime(param) param;

and as a directive itself - without expanding it to some code or value:

#define Test_FuncTime(param)

If we pass as a param an instruction or a function call, it can be expanded in place of macros as code itself, or as nothing. So, I always write these defines in pairs. One of them must be commented. If I want to test something in my application, I write some kind of approving code, which de facto polluted a program code and hamper it's readability. So, when I need a test - I uncomment macros, when I want to hide this extra output - I comment macros and simultaneously uncomment empty define. In my opinion, it is very convenient tool.

Let’s look at it's expanding but still simple usage: To cover any new task with test, you must simply copy-past these declarations and give them appropriate name according to task number or something like that. (Test directive name can contain for example the name of this module of the big project.) If we want some kind of extensive test, we can write a function with as many parameters, as we need and simply call it from macro. I like the following c++ code organization: I have one header for task descriptions, and one for tests declarations (#define Test#123(a) a;) with good comments, describing in general what I want to test. Next, because one compilation unit (a *.cpp file) contains all includes to write a code, at the top of file after them I declare in a separated section all my prototypes. At the end - definitions of all functions with detailed description of what I must receive here!! I do it in every particular function, to quickly remember all after returning to it again. If you want to collect some data and pass it in another part of test - no problem. If your application is a cross platform one, and you want to see test data not in debug but as a program output - use pattern Bridge to output information. That is: abstract base output class, and several children for every particular platform. That's all.

Another feature, that I like in my tests - they are placed in the code just as one line only, which I put after 80-th column - not to see them during work and thinking. Here is the real position of tests in my code:

Image 2

Empty lines at the end of some method definitions (like 19-th and 27-th above) serve for me as a hint – “there are something”. If you want to see them - simply scroll to the right, or look at your tests covering log - which tasks are covered with tests.

Let’s see at example (all examples are included with article as a zip file.):

// tests.h 
// test for trace all stages of class Array functionality:

// constructors, destructors and so on#define Test123_ClassArray(a) a;     
//#define Test123_ClassArray(a) 
// array.cpp

Array::~Array()
{   
    delete [] m_pAr; 
    // I push all test macroses to the right at >= 80-th column
    Test123_ClassArray(cout<<"~Array()"<<this<<' '<<m_nSize<<endl);
}

Of course, this isn't a real test. In this way I explain to my students all stages of object’s life and when and what class functionality (constructors, destructors and so on) are called. So, such tests are included in every studied places (in every files). You can switch them ON:

#define Test123_ClassArray(a) a;
//#define Test123_ClassArray(a) 

and see: in main

C++
{
       std::vector<Array> vc;
       int nCount = 2;
       for(int i = 0; i < nCount; ++i)
       {
           Test123_ClassArray(cout<<"-- before push: size cap "\
           <<vc.size()<<' '<<vc.capacity()<<endl);
           vc.push_back(Array(rand()% 21));
           Test123_ClassArray(cout<<"-- after push: size cap "\
           <<vc.size()<<' '<<vc.capacity()<<"\n\n");
       }
 
       for(int i = 0; i < nCount; ++i)
           vc[i].Show();
}

which will produce:

Image 3

or OFF:

//#define Test123_ClassArray(a) a;
#define Test123_ClassArray(a) 

And receive only

Image 4

The last trick: Template for overloading operator =.

We have to overload operator = if we are allocating resource and are not against to allow for users of our class to call it. (we can forbid usage of this operator.) A classical actions sequence:

  1. check – are we assigning an object to itself?;
  2. allocate exactly the same resources as has right operand;
  3. free our own resource;
  4. deep copy;
  5. return *this;

But... What if a class has a const member – so called readonly field? (c# has this keyword ) It is a convenient tool if we want to set a member variable in runtime and guaranty, that it will remain unchanged until death of this object.

class Array
{
    int m_nSize;
    int * m_pAr;
    const int m_nReadonly;

In this case any attempts to reassign it in our overloaded operator = will make our compiler angry. There are two solutions: 1) Follow the intention of this tool - if you use const member – don’t change it!!! You must redesign your program. 2) If such reassignment is not a work around, but completely suitable to your program logic, you must remember about copy constructor.

I’m intentionally in overloading sequence put allocating a new resource before it will be free . This is related to exception and transaction safety – we are guarantying that left operand will have resource anyway. (If according to logic our left operand have a choice: store the old resource or receive a new one.) So, we will use copy constructor to create a temporary object because:

  1. We will in this way create a new resource before we decide to delete former.
  2. In overloaded operator = we will perform deep copy, which is already well done in copy constructor. So, we will reuse existed code.(By the way, as a rule, if we decided to allow usage a copy construction of our objects, we will give to user a possibility to assign it too and vice versa.)
  3. This is a completely legitimate way to reassign const member. (There is no need to hack it.) First variant:
Array & Array::operator = (const Array & Right)
{
   if(this != &Right)
   {
      Array temp(Right);
      char sBuff[sizeof(Array)];
      memcpy(sBuff, this, sizeof(Array));
      memcpy(this, &temp, sizeof(Array));
      memcpy(&temp, sBuff, sizeof(Array));
   }

   return *this;
}

Here I create deep copy of Right object, create buffer sBuff to swap left operand (*this) with temp and perform the swap itself. At closed bracket “}” temp will die, as any variable allocated on stack, and de facto will solve all our problems with old resources.

This action sequence is common for all classes, so let it be a template:

template <typename T>
void TSwap(T * pThis, const T & Right)
{
   T temp(Right);
   char sBuff[sizeof(T)];
   memcpy(sBuff, pThis, sizeof(T));
   memcpy(pThis, &temp, sizeof(T));
   memcpy(&temp, sBuff, sizeof(T));
}

Now a modified operator = will look like this:

Array & Array::operator = (const Array & Right)
{
   if(this != &Right)    
      TSwap(this, Right);

   return *this;
} 

All functionality was tested on Windows XP, 7 in VS2010 and VS2012 (Using these compilers you must comment keyword noexcept in class Array move constructor due to not complete support of c+11 standard by Microsoft.). and on Ubuntu 14.04 with gcc 4.8.2 (here all works as it is in .zip file.).

Good luck!!

License

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