template with stdarg: enum problem

K

Klaus Schneider

Hi all!

I'm having trouble with a template function with variable arguments when I
parse an enum type as variable argument. Example:

template <class T>
bool test(int num, ...)
{
va_list ap;
int ind;
bool allequal = true;

va_start(ap, num);
T lastentry((T)va_arg(ap, T));
for(ind=0; ind<num; ind++) {
T entry((T)va_arg(ap, T));
/* do something with T */
if (lastentry != entry)
allequal = false;
lastentry = entry;
}
va_end(ap);

return allequal;
}

Now, if I call this template using an enum, as
typedef enum { ONE, TWO } TestEnum;
if (test<TestEnum>(3, ONE, TWO, ONE) == true)
cout << "all equal!\n";

the compiler says
stdarg.cpp: In function `bool test(int, ...) [with T = TestEnum]':
stdarg.cpp:13: instantiated from here
stdarg.cpp:29: warning: `TestEnum' is promoted to `int' when passed through
`...'
stdarg.cpp:29: warning: (so you should pass `int' not `TestEnum' to
`va_arg')
stdarg.cpp:31: warning: `TestEnum' is promoted to `int' when passed through
`...'

and running the program produces a SIGSEGV.

If I change the va_arg(...) lines to
va_arg(ap, int)

it works, but then the template does not work with other types any more.
Thus, I think I need to detect an enum type somehow, but how could I do
that? I searched through the FAQs and such but couldn't find help.

Thanks very much,
Klaus
 
M

mlimber

Klaus said:
Hi all!

I'm having trouble with a template function with variable arguments when I
parse an enum type as variable argument. Example:

template <class T>
bool test(int num, ...)
{
va_list ap;
int ind;
bool allequal = true;

va_start(ap, num);
T lastentry((T)va_arg(ap, T));
for(ind=0; ind<num; ind++) {
T entry((T)va_arg(ap, T));
/* do something with T */
if (lastentry != entry)
allequal = false;
lastentry = entry;
}
va_end(ap);

return allequal;
}

Now, if I call this template using an enum, as
typedef enum { ONE, TWO } TestEnum;
if (test<TestEnum>(3, ONE, TWO, ONE) == true)
cout << "all equal!\n";

the compiler says
stdarg.cpp: In function `bool test(int, ...) [with T = TestEnum]':
stdarg.cpp:13: instantiated from here
stdarg.cpp:29: warning: `TestEnum' is promoted to `int' when passed through
`...'
stdarg.cpp:29: warning: (so you should pass `int' not `TestEnum' to
`va_arg')
stdarg.cpp:31: warning: `TestEnum' is promoted to `int' when passed through
`...'

and running the program produces a SIGSEGV.

If I change the va_arg(...) lines to
va_arg(ap, int)

it works, but then the template does not work with other types any more.
Thus, I think I need to detect an enum type somehow, but how could I do
that? I searched through the FAQs and such but couldn't find help.

Thanks very much,
Klaus

Using va_arg is very much discouraged in C++ because it is not
typesafe. Mixing it with templates, as the FAQs would say, might be
legal but it certainly ain't moral! What is the problem that
necessitates va_args? Can you do it another way?

Cheers! --M
 
K

Klaus Schneider

Hi!

I want to create a vector with any number of arguments in one call, i.e.
typedef { TEMPERATURE, POTENTIAL, OUTFLOW } DataType;
Vector<DataType> v(4, TEMPERATURE, POTENTIAL, TEMPERATURE, OUTFLOW);

As it might have any number of elements, it's impossible to use overloading.
If there is another solution, I would prefer that, but I couldn't think of
one.

Thanks very much,
Klaus
 
G

Greg Comeau

I'm having trouble with a template function with variable arguments when I
parse an enum type as variable argument. Example:

template <class T>
bool test(int num, ...)
{
va_list ap;
int ind;
bool allequal = true;

va_start(ap, num);
T lastentry((T)va_arg(ap, T));
for(ind=0; ind<num; ind++) {
T entry((T)va_arg(ap, T));
/* do something with T */
if (lastentry != entry)
allequal = false;
lastentry = entry;
}
va_end(ap);

return allequal;
}

Now, if I call this template using an enum, as
typedef enum { ONE, TWO } TestEnum;
if (test<TestEnum>(3, ONE, TWO, ONE) == true)
cout << "all equal!\n";

the compiler says
stdarg.cpp: In function `bool test(int, ...) [with T = TestEnum]':
stdarg.cpp:13: instantiated from here
stdarg.cpp:29: warning: `TestEnum' is promoted to `int' when passed through
`...'
stdarg.cpp:29: warning: (so you should pass `int' not `TestEnum' to
`va_arg')
stdarg.cpp:31: warning: `TestEnum' is promoted to `int' when passed through
`...'

and running the program produces a SIGSEGV.

If I change the va_arg(...) lines to
va_arg(ap, int)

it works, but then the template does not work with other types any more.
Thus, I think I need to detect an enum type somehow, but how could I do
that? I searched through the FAQs and such but couldn't find help.

From what I can see, the problem is that in C an enum might be
an int, but in C++ enum's are allowed to be shorter, that is chars.
Furthermore, enum's are promoted when passed to varags functions.
Therefore, your template is conceptually correct but implementation
wise the va_arg results in a misspeak (char vs int), hence the crash.
I seem to recall that gcc has a way to metabolize enum's but don't
know if that's something you want to consider. Then again,
I may not be stareing at your code long enough, and have gotten the
above incorrect.
 
G

Greg Comeau

Hi!

I want to create a vector with any number of arguments in one call, i.e.
typedef { TEMPERATURE, POTENTIAL, OUTFLOW } DataType;
Vector<DataType> v(4, TEMPERATURE, POTENTIAL, TEMPERATURE, OUTFLOW);

As it might have any number of elements, it's impossible to use overloading.
If there is another solution, I would prefer that, but I couldn't think of
one.

Not knowing where you're full code is eventually going,
it may be that you're requiremens outgrow enum's and move
into say a std::vector or std::map?
 
M

Marcus Kwok

Greg Comeau said:
Not knowing where you're full code is eventually going,
it may be that you're requiremens outgrow enum's and move
into say a std::vector or std::map?

This sounds like it might be a reasonable solution to the OP's problem.
Push all your values into a std::vector and then iterate over the vector
in the comparison function. There may be a really simple solution using
something in <algorithm>.
 
M

mlimber

Klaus said:
Hi!

I want to create a vector with any number of arguments in one call, i.e.
typedef { TEMPERATURE, POTENTIAL, OUTFLOW } DataType;
Vector<DataType> v(4, TEMPERATURE, POTENTIAL, TEMPERATURE, OUTFLOW);

As it might have any number of elements, it's impossible to use overloading.
If there is another solution, I would prefer that, but I couldn't think of
one.

Thanks very much,
Klaus

Hi, Klaus. Please put your responses below the quoted text. Top posting
is considered impolite.

There are several approaches beyond the dreaded elipsis. First, you
might consider the Boost.Assignment library
(http://www.boost.org/libs/assign/doc/). It allows you to write code
like this:

std::vector<int> v;
v += 1,2,3,4,5,6,7,8,9;

Alternately, you can roll your own intialization with method chaining
(cf. http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18):

#include <vector>
using namespace std;

template<typename T>
class Initializer
{
vector<T> v_;
public:
Initializer& Add( const T& t ) { v_.push_back(t); return *this; }
operator vector<T>() const { return v_; }
};

// Note: no need for typedef for the enum in C++
enum DataType { TEMPERATURE, POTENTIAL, OUTFLOW };

int main()
{
vector<DataType> v = Initializer<DataType>()
.Add( TEMPERATURE )
.Add( POTENTIAL )
.Add( TEMPERATURE )
.Add( OUTFLOW );
// ...
return 0;
}

Cheers! --M
 
K

Klaus Schneider

Not knowing where you're full code is eventually going,
This sounds like it might be a reasonable solution to the OP's problem.
Push all your values into a std::vector and then iterate over the vector
in the comparison function. There may be a really simple solution using
something in <algorithm>.
It seems you misunderstood what I was trying to do. I have
template <class T>
class Vector : public QValueVector<T>
{
/* just add a new constructor with variable arguments */
};
typedef { TEMPERATURE, POTENTIAL, OUTFLOW } DataType;

I want to have a function
loadData(const char* filename, Vector<DataType> type, Vector<int> indices)
which I want to call as
loadData("experiment.dat",
Vector<DataType>(4,TEMPERATURE, POTENTIAL, TEMPERATURE, OUTFLOW),
Vector<int>(4, 7, 10, 5, 2));

Of course I could do
QValueVector<DataType> types(4);
types.append(TEMPERATURE);
types.append(POTENTIAL);
types.append(OUTFLOW);
types.append(TEMPERATURE);
QValueVector<int> indices(4);
indices.append(7);
indices.append(10);
indices.append(5);
indices.append(2);
loadData(filename, types, indices);

but this is confusing because you don't see the structure of the data
without looking twice. Thus, I thought I'd enhance QValueVector by a new
constructor... but somehow it didn't work as I thought it would.

Do you have suggestions for a "clean" solution to the problem?

Thanks,
Klaus
 
K

Klaus Schneider

mlimber said:
Hi, Klaus. Please put your responses below the quoted text. Top posting
is considered impolite.

There are several approaches beyond the dreaded elipsis. First, you
might consider the Boost.Assignment library
(http://www.boost.org/libs/assign/doc/). It allows you to write code
like this:

std::vector<int> v;
v += 1,2,3,4,5,6,7,8,9;

Alternately, you can roll your own intialization with method chaining
(cf. http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18):

#include <vector>
using namespace std;

template<typename T>
class Initializer
{
vector<T> v_;
public:
Initializer& Add( const T& t ) { v_.push_back(t); return *this; }
operator vector<T>() const { return v_; }
};

// Note: no need for typedef for the enum in C++
enum DataType { TEMPERATURE, POTENTIAL, OUTFLOW };

int main()
{
vector<DataType> v = Initializer<DataType>()
.Add( TEMPERATURE )
.Add( POTENTIAL )
.Add( TEMPERATURE )
.Add( OUTFLOW );
// ...
return 0;
}

Thanks very much! That is really what I needed...
Klaus
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top