Page 1 of 1

How to hide the same code (not using MACRO) ?

Posted: Thu Oct 10, 2013 7:42 am
by acDev
Client-side code:

Code: Select all

// interface (C-style)
int NI_Print(const std::string & text);
int NI_PrintSL(const QStringList & text);

int NI_Print(const std::string & text)
{
  int hr = NOERROR;
  int xi = 0;
  do {
    try
    {
      hr = g_rcfclnt->Print(text);
    }
    catch (const RCF::Exception & e)
    {  
      hr = E_FAIL;
      if (!xi && e.getErrorId() != RCF::RcfError_ClientReadFail) xi++;  // xi = 1;
      xi++;
      if (xi > 1) NI_SetLastErrorMsg(e);
    }
    if (xi > 1) break;    // when an error number 31, try running the query again
  } while (xi > 0);  
  return hr;
}

int NI_PrintSL(const QStringList & text)
{
  int hr = NOERROR;
  int xi = 0;
  do {
    try
    {
      hr = g_rcfclnt->PrintSL(text);
    }
    catch (const RCF::Exception & e)
    {     
      hr = E_FAIL;
      if (!xi && e.getErrorId() != RCF::RcfError_ClientReadFail) xi++;  // xi = 1;
      xi++;
      if (xi > 1) NI_SetLastErrorMsg(e);
    }
    if (xi > 1) break;    // when an error number 31, try running the query again
  } while (xi > 0);  
  return hr;
}
How to hide the same code?
I'm not good in the C++, boost, BOOSTPP and other.

Re: How to hide the same code (not using MACRO) ?

Posted: Thu Oct 10, 2013 8:24 am
by jarl
This will remove the duplication for you:

Code: Select all

int doPrint(const std::string & text)
{
	int hr = g_rcfclnt->Print(text);
	return hr;
}

int doPrint(const QStringList & text)
{
	int hr = g_rcfclnt->PrintSL(text);
	return hr;
}

template<typename T>
int NI_Print(const T & text)
{
	int hr = NOERROR;
	int xi = 0;
	do {
		try
		{
			hr = doPrint(text);
		}
		catch (const RCF::Exception & e)
		{  
			hr = E_FAIL;
			if (!xi && e.getErrorId() != RCF::RcfError_ClientReadFail) xi++;  // xi = 1;
			xi++;
			if (xi > 1) NI_SetLastErrorMsg(e);
		}
		if (xi > 1) break;    // when an error number 31, try running the query again
	} while (xi > 0);  
	return hr;
}

Re: How to hide the same code (not using MACRO) ?

Posted: Thu Oct 10, 2013 9:30 am
by acDev
jarl wrote:This will remove the duplication for you:

Code: Select all

....
But if the features have different number of arguments?

Now I've done this:

Code: Select all

#define M_PREFIX                  \
  int hr = NOERROR;               \
  int xi = 0;                     \
  do {                            \
    try {

#define M_POSTFIX                        \
    }                                    \
    catch (const RCF::Exception & e)     \
    {                                    \
      hr = E_FAIL;                       \
      if (!xi && e.getErrorId() != RCF::RcfError_ClientReadFail) xi++; \
      xi++;                              \
      if (xi > 1) NI_SetLastErrorMsg(e); \
    }                                    \
    if (xi > 1) break;   /* when an error number 31, try running the query again */ \
  } while (xi > 0);                      \
  return hr
  
#define RCFCLIENT_METHOD_R1(method,v1) \
  M_PREFIX hr = g_rcfclnt->##method((v1)); M_POSTFIX
  
#define RCFCLIENT_METHOD_R2(method,v1,v2) \
  M_PREFIX hr = g_rcfclnt->##method((v1),(v2)); M_POSTFIX
  
#define RCFCLIENT_METHOD_R3(method,v1,v2,v3) \
  M_PREFIX hr = g_rcfclnt->##method((v1),(v2),(v3)); M_POSTFIX
  
#define RCFCLIENT_METHOD_R4(method,v1,v2,v3,v4) \
  M_PREFIX hr = g_rcfclnt->##method((v1),(v2),(v3),(v4)); M_POSTFIX
  
#define RCFCLIENT_METHOD_R5(method,v1,v2,v3,v4,v5) \
  M_PREFIX hr = g_rcfclnt->##method((v1),(v2),(v3),(v4),(v5)); M_POSTFIX

int NI_Print(const std::string & text)
{
  RCFCLIENT_METHOD_R1(Print, text);
}

int NI_PrintSL(const QStringList & text)
{
  RCFCLIENT_METHOD_R1(PrintSL, text);
}
It's better?

Re: How to hide the same code (not using MACRO) ?

Posted: Fri Oct 11, 2013 4:00 am
by jarl
It's not so easy in C++ to factor out try/catch wrappers like you are wanting to do here. It can be done by wrapping the RCF call with boost::function<> and boost::bind, and then passing that as a parameter to a common try/catch wrapper. However wrapping arguments with boost::bind can be pretty intricate, if you haven't used it before.

A simpler way to factor out at least part of the try/catch is the following:

Code: Select all

bool catchHandler(int numberOfAttempts)
{
	bool shouldRetry = false;
	try
	{
		throw;
	}
	catch(const std::exception & e)
	{
		// Common catch logic here.
		// ...

		shouldRetry =  true;
	}
	return shouldRetry;
}

void NI_Print(const std::string & text)
{
	int hr = 0;
	bool shouldRetry = true;
	int numberOfAttempts = 0;
	while (shouldRetry)
	{
		++numberOfAttempts;
		try
		{
			g_rcfclnt->Print(text);
		}
		catch(...)
		{
			shouldRetry = catchHandler(numberOfAttempts);
		}
	}
}
You will still have some duplicated code, but it's fairly limited and only contains boilerplate. Also I wouldn't bother with any macros. Although they can save you a couple of minutes when you're writing the code, they are likely to cost you much more time than that when you are maintaining the code later on.