template with stdarg: enum problem

Discussion in 'C++' started by Klaus Schneider, Oct 12, 2005.

  1. 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
    Klaus Schneider, Oct 12, 2005
    #1
    1. Advertising

  2. Klaus Schneider

    mlimber Guest

    Klaus Schneider wrote:
    > 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
    mlimber, Oct 12, 2005
    #2
    1. Advertising

  3. 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

    mlimber wrote:
    > 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?
    Klaus Schneider, Oct 12, 2005
    #3
  4. Klaus Schneider

    Greg Comeau Guest

    In article <dij2l2$2au$-heidelberg.de>,
    Klaus Schneider <-heidelberg.de.x> wrote:
    >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.
    --
    Greg Comeau / Celebrating 20 years of Comeauity!
    Comeau C/C++ ONLINE ==> http://www.comeaucomputing.com/tryitout
    World Class Compilers: Breathtaking C++, Amazing C99, Fabulous C90.
    Comeau C/C++ with Dinkumware's Libraries... Have you tried it?
    Greg Comeau, Oct 12, 2005
    #4
  5. Klaus Schneider

    Greg Comeau Guest

    In article <dij4r4$2lo$-heidelberg.de>,
    Klaus Schneider <-heidelberg.de.x> wrote:
    >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?
    --
    Greg Comeau / Celebrating 20 years of Comeauity!
    Comeau C/C++ ONLINE ==> http://www.comeaucomputing.com/tryitout
    World Class Compilers: Breathtaking C++, Amazing C99, Fabulous C90.
    Comeau C/C++ with Dinkumware's Libraries... Have you tried it?
    Greg Comeau, Oct 12, 2005
    #5
  6. Klaus Schneider

    Marcus Kwok Guest

    In article <dij4r4$2lo$-heidelberg.de>,
    Klaus Schneider <-heidelberg.de.x> wrote:
    >>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.


    Greg Comeau <> wrote:
    > 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>.

    --
    Marcus Kwok
    Marcus Kwok, Oct 12, 2005
    #6
  7. Klaus Schneider

    mlimber Guest

    Klaus Schneider wrote:
    > mlimber wrote:
    > > 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?

    > 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
    mlimber, Oct 12, 2005
    #7
  8. >> 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>.

    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
    Klaus Schneider, Oct 12, 2005
    #8
  9. mlimber wrote:
    > Klaus Schneider wrote:
    >> mlimber wrote:
    >> > 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?

    >> 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;
    > }


    Thanks very much! That is really what I needed...
    Klaus
    Klaus Schneider, Oct 12, 2005
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Francesco Bochicchio

    Equivalent of stdarg.h but on the calling side ?

    Francesco Bochicchio, Jul 1, 2003, in forum: C Programming
    Replies:
    5
    Views:
    916
    Mark Gordon
    Jul 4, 2003
  2. Clint Olsen

    Using stdarg with unknown types

    Clint Olsen, Oct 27, 2003, in forum: C Programming
    Replies:
    6
    Views:
    413
    Sheldon Simms
    Oct 29, 2003
  3. Mac A. Cody

    Confusion with stdarg

    Mac A. Cody, Jan 3, 2005, in forum: C Programming
    Replies:
    9
    Views:
    438
    Lawrence Kirby
    Jan 4, 2005
  4. cman

    stdarg definitions

    cman, Mar 20, 2007, in forum: C Programming
    Replies:
    3
    Views:
    502
    Chris Torek
    Mar 25, 2007
  5. Obnoxious User

    stdarg problems

    Obnoxious User, Jul 1, 2007, in forum: C++
    Replies:
    14
    Views:
    646
    James Kanze
    Jul 3, 2007
Loading...

Share This Page