templates - method return value is multiple types

C

Christopher

Is it possible to create a method that can return different types
using a template?

My goal is to get a value from the Window's Registry in one handy
dandy place. However my question is on template methods, not the
WIndows API.

I suppose I could split this up into one private method and two public
ones, having the private method get a string and the public ones
taking an [out] reference to a string, while the other take an [out]
reference to an int. I'd rather figure out if I can handle it in one
method though. I don't use templates much, so I figured this is an
opportunity to learn more.

I read that the compiler generates code based on the T, so I suppose
the errors I am getting make sense about "return cannot convert from _
to_" and makes me believe what I want here cannot be done with
templates, but I want to be sure there isn't another way using
templates.

Here is what I tried initially:



class RegistrySettings
{
public:

/// The one and only registry key for the .....
static const std::wstring MYCOMPONENT_REG_KEY;

~RegistrySettings();
static const RegistrySettings * Instance();

template <typename T>
const T GetValue(const std::wstring & valueName) const
{
HKEY registryKey = NULL;
DWORD disposition = 0;

if( ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE,

RegistrySettings::MYCOMPONENT_REG_KEY.c_str(),
0,
NULL,

REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&registryKey,
&disposition) )
{
// Unexpected error
}

boost::scoped_array<TCHAR> buffer;
DWORD bufferSize = (MAX_PATH / 2) *
sizeof(TCHAR);
DWORD type = 0;
DWORD result = 0;

do
{
bufferSize *= 2;
buffer.reset(new TCHAR[bufferSize]);

result = RegQueryValueEx(registryKey,
valueName.c_str(),
NULL,
&type,
reinterpret_cast<BYTE
*>(buffer.get()),
&bufferSize);

}while(result == ERROR_MORE_DATA);

if( result != ERROR_SUCCESS )
{
// Unexpected Error
}

if( ERROR_SUCCESS != RegCloseKey(registryKey) )
{
// Unexpected Error
}

switch(type)
{
case REG_DWORD:
return boost::lexical_cast<DWORD>(buffer.get());

case REG_SZ:
return std::wstring(buffer.get());

case REG_EXPAND_SZ:
boost::scoped_array<TCHAR> expandedBuffer;
DWORD expandedBufferSize =
(MAX_PATH / 2) * sizeof(TCHAR);
DWORD characterCount = 0;

characterCount =
ExpandEnvironmentStrings(buffer.get(),

expandedBuffer.get(),

expandedBufferSize);

while( characterCount > expandedBufferSize )
{
// Buffer was too small, retry
// TODO - TEST THIS for infinite loop!!!
expandedBufferSize = characterCount;
expandedBuffer.reset(new
TCHAR[expandedBufferSize]);

characterCount =
ExpandEnvironmentStrings(buffer.get(),

expandedBuffer.get(),

expandedBufferSize);
}

if( !characterCount )
{
// Unexpected Error
}

return std::wstring(expandedBuffer.get());

default:
throw common::NotImplementedException(__WFILE__,
__LINE__) << L"The registry value type cannot be converted." <<
valueName;
}



}

private:

RegistrySettings();
RegistrySettings(const RegistrySettings & rhs);

static const RegistrySettings * m_instance;
};

}


#endif // REGISTRY_STRINGS
 
S

Stefan Ram

Christopher said:
Is it possible to create a method that can return different types

A function can return an instance of any type that is
a subtype of its declared return type (polymorphism).
 
R

Ruben Safir

Stefan Ram said:
A function can return an instance of any type that is
a subtype of its declared return type (polymorphism).


what is a subtype? The template book i have makes it fairly clear that
changing the subtype, even with partial and explicit specialization, is
impossible because it changes the templates signature.

Ruben
 
S

Stefan Ram

Ruben Safir said:
what is a subtype? The template book i have makes it fairly clear that
changing the subtype, even with partial and explicit specialization, is
impossible because it changes the templates signature.

#include <cstdlib>
class A {}; class B: public A {}; class C: public A {};
A f(){ if( ::std::rand() )return B(); else return C(); }
int main(){}
 
S

Stefan Ram

Sam said:
Your function still returns an instance of A, in all cases. Yes, the
"return" statement can evaluate to any subclass.

Here is my next attempt at conveying what I intended to
say in my first reply in this thread: The function f()
can return (via a pointer) an instance of type B or C.

#include <iostream>
#include <ostream>
#include <cstdlib>
#include <string>

struct A { virtual int i(){ return 10; }};
struct B: public A { virtual int i(){ return 11; }};
struct C: public A { virtual int i(){ return 12; }};
A * f(){ if( ::std::rand() )return new B(); else return new C(); }

int main()
{ A * a = f();
::std::cout << typeid( *a ).name() << '\n';
::std::cout << a->i() << '\n';
/* outputs (for example): "1B\n11\n" */
delete a; }
 
S

Stefan Ram

Here is my next attempt at conveying what I intended to
say in my first reply in this thread: The function f()
can return (via a pointer) an instance of type B or C.

The next program tries to avoid dynamic life-time.
I try to use a rule saying that a reference to a temporary
can be bound to a reference to const, but I am not sure
whether I got it right?

#include <iostream>
#include <ostream>
#include <cstdlib>
#include <string>

struct A { virtual int i() const{ return 10; }};
struct B: public A { virtual int i() const{ return 11; }};
struct C: public A { virtual int i() const{ return 12; }};
A const &f(){ if( ::std::rand() )return B(); else return C(); }

int main()
{ A const &a = f();
::std::cout << typeid( a ).name() << '\n';
::std::cout << a.i() << '\n';
/* outputs: "1B\n11\n" */ }
 
C

Christopher

Undefined behavior.

Yes, a reference to a temporary can be bound to a reference to a const. But,  
when your function returns the temporary no longer exists, and the reference  
is no longer valid.

When you pass a temporary to the function call, the temporary is in the  
scope until the next sequence point, which will be after the function call  
returns.

The sequence point in question, here, is the semicolon that terminates the  
return statement, at which point the temporary goes out of scope, and you're  
returning a reference to a non-existent object.

If a compiler's warning/pedantic setting is turned up, some compilers may 
complain here.

 application_pgp-signature_part
< 1KViewDownload

You guys kind of went off on a tangent on derived types. I was asking
about primitive types. I assume what I want isn't possible.
 
S

Stefan Ram

Sam said:
Yes, a reference to a temporary can be bound to a reference to a const. But,
when your function returns the temporary no longer exists, and the reference
is no longer valid.

I was referring to:

http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/

Now, what I really do not understand, is why

#include <iostream>
#include <ostream>
#include <cstdlib>
#include <string>

struct A { virtual int i() const{ return 10; }
virtual ~A(){ ::std::cerr << "destructed." << ::std::endl; }};
struct B: public A { virtual int i() const{ return 11; }};
struct C: public A { virtual int i() const{ return 12; }};
A f(){ return A(); }

int main()
{ const A & a = f();
::std::cerr << typeid( a ).name() << ::std::endl;
::std::cerr << a.i() << ::std::endl;
/* outputs: "1A\n10\n" */ }

does not seem to call the destructor, but

#include <iostream>
#include <ostream>
#include <cstdlib>
#include <string>

struct A { virtual int i() const{ return 10; }
virtual ~A(){ ::std::cerr << "destructed." << ::std::endl; }};
struct B: public A { virtual int i() const{ return 11; }};
struct C: public A { virtual int i() const{ return 12; }};
A f(){ if( ::std::rand() )return B(); else return C(); }

int main()
{ const A & a = f();
::std::cerr << typeid( a ).name() << ::std::endl;
::std::cerr << a.i() << ::std::endl;
/* outputs: "destructed.\n1A\n10" */ }

does seem to call the destructor.

The only difference is the body of »f()«:

A f(){ return A(); }

versus

A f(){ if( ::std::rand() )return B(); else return C(); }
 
V

Victor Bazarov

Is it possible to create a method that can return different types
using a template?

You did.
My goal is to get a value from the Window's Registry in one handy
dandy place. However my question is on template methods, not the
WIndows API.
OK.

I suppose I could split this up into one private method and two public
ones,

Why two?
having the private method get a string and the public ones
taking an [out] reference to a string, while the other take an [out]
reference to an int.

Why a reference? Functions should return values.
I'd rather figure out if I can handle it in one
method though. I don't use templates much, so I figured this is an
opportunity to learn more.

I read that the compiler generates code based on the T, so I suppose
the errors I am getting make sense about "return cannot convert from _
to_" and makes me believe what I want here cannot be done with
templates, but I want to be sure there isn't another way using
templates.

You don't show how you use your function. Please do.
Here is what I tried initially:



class RegistrySettings
{
public:

/// The one and only registry key for the .....
static const std::wstring MYCOMPONENT_REG_KEY;

~RegistrySettings();
static const RegistrySettings * Instance();

template<typename T>
const T GetValue(const std::wstring& valueName) const
{
HKEY registryKey = NULL;
DWORD disposition = 0;

if( ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE,

RegistrySettings::MYCOMPONENT_REG_KEY.c_str(),
0,
NULL,

REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&registryKey,
&disposition) )
{
// Unexpected error
}

boost::scoped_array<TCHAR> buffer;
DWORD bufferSize = (MAX_PATH / 2) *
sizeof(TCHAR);
DWORD type = 0;
DWORD result = 0;

do
{
bufferSize *= 2;
buffer.reset(new TCHAR[bufferSize]);

result = RegQueryValueEx(registryKey,
valueName.c_str(),
NULL,
&type,
reinterpret_cast<BYTE
*>(buffer.get()),
&bufferSize);

}while(result == ERROR_MORE_DATA);

if( result != ERROR_SUCCESS )
{
// Unexpected Error
}

if( ERROR_SUCCESS != RegCloseKey(registryKey) )
{
// Unexpected Error
}

switch(type)
{
case REG_DWORD:
return boost::lexical_cast<DWORD>(buffer.get());

case REG_SZ:
return std::wstring(buffer.get());

case REG_EXPAND_SZ:
boost::scoped_array<TCHAR> expandedBuffer;
DWORD expandedBufferSize =
(MAX_PATH / 2) * sizeof(TCHAR);
DWORD characterCount = 0;

characterCount =
ExpandEnvironmentStrings(buffer.get(),

expandedBuffer.get(),

expandedBufferSize);

while( characterCount> expandedBufferSize )
{
// Buffer was too small, retry
// TODO - TEST THIS for infinite loop!!!
expandedBufferSize = characterCount;
expandedBuffer.reset(new
TCHAR[expandedBufferSize]);

characterCount =
ExpandEnvironmentStrings(buffer.get(),

expandedBuffer.get(),

expandedBufferSize);
}

if( !characterCount )
{
// Unexpected Error
}

return std::wstring(expandedBuffer.get());

default:
throw common::NotImplementedException(__WFILE__,
__LINE__)<< L"The registry value type cannot be converted."<<
valueName;
}



}

private:

RegistrySettings();
RegistrySettings(const RegistrySettings& rhs);

static const RegistrySettings * m_instance;
};

}


#endif // REGISTRY_STRINGS
^^^^^^ What does this '#endif' close? I don't see an #ifxxx

I don't see the complete code. See FAQ 5.8.

V
 
S

Stefan Ram

Christopher said:
You guys kind of went off on a tangent on derived types. I was asking
about primitive types. I assume what I want isn't possible.

You can »return« a value of either »int« or »double« type in a
certain figurative sense:

I call »callee«, which will »return« either an »int« or a »double«:

callee( this );
::std::cout << num->type() << '\n';
::std::cout << num->text() << '\n';

. This will print either

int
1

or

double
1

. The callee is looking like:

void callee( visitor * const v )
{ if( ::std::rand() )v->accept( 1 );
else v->accept( 1.0 ); }

, instead of an actual return, he uses »v->accept«.

Now, the complete program:

#include <iostream>
#include <ostream>
#include <cstdlib>
#include <string>
#include <sstream>

struct visitor
{ virtual void accept( int const value )= 0;
virtual void accept( double const value )= 0; };

struct number
{ virtual ::std::string type()= 0;
virtual ::std::string text()= 0; };

struct integer : public number
{ int val;
integer( int const n ): val( n ){};
virtual ::std::string type(){ return "int"; };
virtual ::std::string text(){ std::stringstream out;
out << val; return out.str(); }};

struct floating : public number
{ double val;
floating( double const n ): val( n ){};
virtual ::std::string type(){ return "double"; };
virtual ::std::string text(){ std::stringstream out;
out << val; return out.str(); }};

void callee( visitor * const v )
{ if( ::std::rand() )v->accept( 1 );
else v->accept( 1.0 ); }

struct caller : public visitor
{ number * num;
void accept( int const value ){ this->num = new integer( value ); };
void accept( double const value ){ this->num = new floating( value ); };
void example()
{ callee( this );
::std::cout << num->type() << '\n';
::std::cout << num->text() << '\n'; }};

int main(){ caller().example(); }
 
P

Parkitesta Arctonium

Stefan Ram said:
#include <cstdlib>
class A {}; class B: public A {}; class C: public A {};
A f(){ if( ::std::rand() )return B(); else return C(); }
int main(){}

Epic Fail.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top