C++ templates & call by reference

Hello!

I encountered an interesting phenomenon today while writing on a C++ project using Microsoft Visual Studio 2008.

Short description:
I have a method called ReadValue for several types. As it is not vise to have code repetitions, I decided to use templates.

The method is declared as

template <class T>
bool ReadValue(const LPCTSTR szName, T &nValue, const T nDefValue);

and uses call by reference, as you can see.

The Problem:
Today, I came across the problem. Althogh the method reads successfully the values, the found value is not returned over nValue. I immediately recreated the normal methods for the different types (BYTE,  float,  int,  UINT,  WORD, ...) & commented the template method ReadValue, everything was OK & the value was returned to the calling variable

My question:
What am I doing wrong?

Thank you for your help!
// -------------------------------------------------------------------------------------------
// Base functionality of getting a number from the data file.
// Parameters:
// szName : String of the name to be searched in the data file.
// nValue : Value to be returned
// nDefault: Value to be taken, if the nValue does not exist in the data file.
// Returns whether the reading was successful of not.
// 20100213 SAE
// -------------------------------------------------------------------------------------------
template <class T>
bool CPSDatFile::ReadValue(const LPCTSTR szName, T &nValue, const T nDefault)
{
bool bValueRead = false;
stlString strBuffer;

if(ReadValue(szName, strBuffer, ""))
{
nValue = (T)_tstof(strBuffer.c_str());
bValueRead = true;
}
else
{
nValue = nDefault;
}

return bValueRead;
}

Open in new window

LVL 1
Ahmet Ekrem SABANSenior IT consultantAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

evilrixSenior Software Engineer (Avast)Commented:
For a template function it's doing things that are very type specific. It's assuming that T is a float. Shouldn't this be a specialisation?
0
w00teCommented:
I don't know if this is the problem or not, but I think if I came across this line I would do something to get around this c-style cast.  It looks like something that would cause problems given your range of types, and c-style casts are really deprecated anyway.  You should use a stringstream for the converstion if at all possible.
(T)_tstof(strBuffer.c_str());  
Using other methods may at least give you a more detailed error message.  I really don't think that that line would work the way you tried to use it though.  Evilrix can comment, I'm sure he'll know better than I do.
0
itsmeandnobodyelseCommented:
>>>> I have a method called ReadValue for several types. As it is not vise to have code repetitions, I decided to use templates.

If using the template function for different types you must have *removed* all the specific functions thoroughly from your project or you may get a mix which hardly could resolved by the Debugger. In other words, if you encountered the phenomen with the debugger while the sources for your specific ReadValue sources still were existing as object files or even bound to the project, your debug results most probably are wrong.

>>>> nValue = (T)_tstof(strBuffer.c_str());

To add to evilrix' comment:

You know, any of your types used should take a float as input. So, if using class types they must have a constructor that takes a float (and not a double or an int).
0
Cloud Class® Course: C++ 11 Fundamentals

This course will introduce you to C++ 11 and teach you about syntax fundamentals.

evilrixSenior Software Engineer (Avast)Commented:
Put simply, this template function is badly designed. It is not generic... it is a generic wrapper for code that is trying to coerse to a specific type. Even if we can figure out why you are seeing the behaviour you are my strong advice would be to resoncider what you are doing here.

>> you must have *removed* all the specific functions thoroughly from your project or you may get a mix which hardly could resolved

That is a good point, although the behaviour in this situation is well defined. Concrete functions always take priority over template functions. Or, put another way, the template will only be instantiated if the compiler cannot find a concrete function that will satisfy the criteria.
#include <iostream>


template <typename T>
void foo(T)
{
   std::cout << "fooT" << std::endl;
}

void foo(int)
{
   std::cout << "foo" << std::endl;
}


int main()
{
   foo(int(1)); // Will call foo and not fooT
}

Open in new window

0
Ahmet Ekrem SABANSenior IT consultantAuthor Commented:
Thank you for your replies!
I do remove the non-template set of methods when I introduce the template. So, the compiler does not have any problems. You are pointing out that the deprecated cast is the source, so I will try to solve it without the cast.
0
w00teCommented:
This is an impressive comment set for a 50 mark question haha. :)
0
Ahmet Ekrem SABANSenior IT consultantAuthor Commented:
By the way: How can I get the appropriate number out of a string in C++ without using the deprecated C functions?
0
itsmeandnobodyelseCommented:
>>>> How can I get the appropriate number out of a string in C++ without using the deprecated C functions?

You can use istringstream

     istringstream iss(strBuffer);
     float f;
     if (!(iss >> f))
     {
           // conversion failed
     
0
evilrixSenior Software Engineer (Avast)Commented:
To add to the above comment.

Below is an example of the stringstream solution added to your code (untested).

For non intrinsic types you'll need to create a specialisation to handle conversion.
template <class T>
bool CPSDatFile::ReadValue(const LPCTSTR szName, T &nValue, const T nDefault)
{
   bool bValueRead = false;
   stlString strBuffer;

   if(ReadValue(szName, strBuffer, ""))
   {
      // Like what Alex suggested but specific to our problem...

      std::istringstream iss(strBuffer);
      if(!(iss >> nValue))
      {
         // Handle convertion error
      }

      bValueRead = true;
   }
   else
   {
      nValue = nDefault;
   }

   return bValueRead;
}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
itsmeandnobodyelseCommented:
istringstream was available thru <sstream> header.

>>>> You are pointing out that the deprecated cast is the source, so I will try to solve it without the cast.

The cast is only a problem cause it restricts the template types to those types which could be converted from a float. If for example you would use template type 'const char *' or std::string, or std::vector, or bool, or .... the function would fail or give unpredictable results.

A template function best should not make any assumptions for their types but support any type. Sometimes it might make sense to allow only class types which were derived from a certain baseclass. You could force such a restriction for example by a statement like

    Baseclass * p = &value;

which would fail at compile time if a type was used which was not derived from Baseclass. Applying to your function it would mean if you only want to have template types that could be converted from float, you should omit the cast and simply assign the float to the type. Then, the compiler would reject any type that has no valid assignment operator.


0
Ahmet Ekrem SABANSenior IT consultantAuthor Commented:
Thank you very much, guys! The code I am using has a lot of C stuff that is deprecated, but I just used the code of an existing project to adopt it to the new functional specification.
evilrix, thank you for your code. With an #include <sstream>, it worked perfectly.
To make a just point distribution, I increase the points to 100 first...
0
evilrixSenior Software Engineer (Avast)Commented:
>> To make a just point distribution, I increase the points to 100 first...
Thank you sae1962at, although your thanks and knowing we solved your issue is reward enough.

Best regards.
0
evilrixSenior Software Engineer (Avast)Commented:
BTW: if you have any continued problems with this don't hesitate to post back here.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.