Thread overview
variable argument lists
May 23, 2003
Ilya Minkov
May 24, 2003
Sean L. Palmer
May 23, 2003
I did not find them in D, and I could not find a relevant post in the newsgroups, but I assume I did not look hard enough. Anyway, if it has not been already proposed, here is my proposition to put variable argument declarations in D:

A method that receives variable arguments (printf, for example), can be declared with a single parameter '...',as in C/C++. There is no need to declare another parameter first though. Before putting the arguments on the stack, the compiler builds a table of argument information. Each entry in this table contains the type id of the entity passed on the stack and its address on the stack. Then, the programmer can use these information for accessing the objects in the stack in a safe manner. The syntax should be similar to accessing an array, only the name being predefined and considered a property of the method. For example:

//class with method of variable arguments
class ABC
{
    //method with variable arguments
    void vaMethod(...)
    {
        for(int i = 0; i < params.size; i++)
        {
            switch (params[i].type)
            {
                case int:
                    int v = params[i];
                    break;
                case char:
                    char v = params[i];
                    break;
                case long:
                    long v = params[i];
                    break;
            }
        }
    }
};

//usage
ABC obj;
obj.vaMethod(1, 2, "string", 5.6);

Of course, this technique means the following:

1) that during compilation, D enumerates all types available to the
application and uses this implicit enumeration in pushing to the stack any
arguments.
2) each method gets a static property which is a static array of arguments;
array semantics are preserved.
3) when a parameter is accessed as a type other than what it really is, an
exception should be thrown.
4) variable argument passing becomes type safe.
5) the need for format strings and parsing is (partially!!!) eliminated.
6) the D compiler must understand a special switch statement that concerns
the type of the variable; this is almost the same as run-time type
identification.

This method can replace printf: string formatters can become objects passed on the stack that contain the value to be printed and return a string representation of the value with the given format parameters.

Here is the relevant C++ example program; of course, since the compiler does not support this technique, everything is done manually: the 'param_list' class accepts up to 10 arguments, all of predefined type. Also note the existence of a 'variant' class:

#include <vector>
#include <string>
#include <stdexcept>
#include <iostream>
using namespace std;

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;
typedef unsigned int uint;

enum type_enum {
    t_none,
    t_char,
    t_short,
    t_long,
    t_int,
    t_byte,
    t_word,
    t_dword,
    t_uint,
    t_string
};

class type_error : public runtime_error {
public:
    type_error(const string &type) : runtime_error("invalid type:" + type) {
    }
};

class variant {
public:
    variant() {
        _type = t_none;
        _value.v_int = 0;
    }

    variant(char v) {
        _type = t_char;
        _value.v_char = v;
    }

    variant(short v) {
        _type = t_short;
        _value.v_short = v;
    }

    variant(long v) {
        _type = t_long;
        _value.v_long = v;
    }

    variant(int v) {
        _type = t_int;
        _value.v_int = v;
    }

    variant(byte v) {
        _type = t_byte;
        _value.v_byte = v;
    }

    variant(word v) {
        _type = t_word;
        _value.v_word = v;
    }

    variant(dword v) {
        _type = t_dword;
        _value.v_dword = v;
    }

    variant(uint v) {
        _type = t_uint;
        _value.v_uint = v;
    }

    variant(const string &v) {
        _type = t_string;
        _string = v;
    }

    variant(const char *s) {
        _type = t_string;
        _string = s;
    }

    operator char () const {
        if (_type != t_char) throw type_error(_type_name(_type));
        return _value.v_char;
    }

    operator short () const {
        if (_type != t_short) throw type_error(_type_name(_type));
        return _value.v_short;
    }

    operator long () const {
        if (_type != t_long) throw type_error(_type_name(_type));
        return _value.v_long;
    }

    operator int () const {
        if (_type != t_int) throw type_error(_type_name(_type));
        return _value.v_int;
    }

    operator byte () const {
        if (_type != t_byte) throw type_error(_type_name(_type));
        return _value.v_byte;
    }

    operator word () const {
        if (_type != t_word) throw type_error(_type_name(_type));
        return _value.v_word;
    }

    operator dword () const {
        if (_type != t_dword) throw type_error(_type_name(_type));
        return _value.v_dword;
    }

    operator uint () const {
        if (_type != t_uint) throw type_error(_type_name(_type));
        return _value.v_uint;
    }

    operator string () const {
        if (_type != t_string) throw type_error(_type_name(_type));
        return _string;
    }

    int type() const {
        return _type;
    }

private:
    union _Variant {
        char v_char;
        short v_short;
        long v_long;
        int v_int;
        byte v_byte;
        word v_word;
        dword v_dword;
        uint v_uint;
    };

    int _type;
    _Variant _value;
    string _string;

    static string _type_name(int type) {
        switch (type) {
            case t_none: return "none";
            case t_char: return "char";
            case t_short: return "short";
            case t_long: return "long";
            case t_int: return "int";
            case t_byte: return "byte";
            case t_word: return "word";
            case t_dword: return "dword";
            case t_uint: return "uint";
            case t_string: return "string";
        }
        return "";
    }
};

class param_list : public vector<variant> {
public:
    param_list(const variant &v1) {
        push_back(v1);
    }

    param_list(const variant &v1, const variant &v2) {
        push_back(v1);
        push_back(v2);
    }

    param_list(const variant &v1, const variant &v2, const variant &v3) {
        push_back(v1);
        push_back(v2);
        push_back(v3);
    }

    param_list(const variant &v1, const variant &v2, const variant &v3,
const variant &v4) {
        push_back(v1);
        push_back(v2);
        push_back(v3);
        push_back(v4);
    }

    param_list(const variant &v1, const variant &v2, const variant &v3,
const variant &v4, const variant &v5) {
        push_back(v1);
        push_back(v2);
        push_back(v3);
        push_back(v4);
        push_back(v5);
    }

    param_list(const variant &v1, const variant &v2, const variant &v3,
const variant &v4, const variant &v5, const variant &v6) {
        push_back(v1);
        push_back(v2);
        push_back(v3);
        push_back(v4);
        push_back(v5);
        push_back(v6);
    }

    param_list(const variant &v1, const variant &v2, const variant &v3,
const variant &v4, const variant &v5, const variant &v6, const variant &v7)
{
        push_back(v1);
        push_back(v2);
        push_back(v3);
        push_back(v4);
        push_back(v5);
        push_back(v6);
        push_back(v7);
    }

    param_list(const variant &v1, const variant &v2, const variant &v3,
const variant &v4, const variant &v5, const variant &v6, const variant &v7,
const variant &v8) {
        push_back(v1);
        push_back(v2);
        push_back(v3);
        push_back(v4);
        push_back(v5);
        push_back(v6);
        push_back(v7);
        push_back(v8);
    }

    param_list(const variant &v1, const variant &v2, const variant &v3,
const variant &v4, const variant &v5, const variant &v6, const variant &v7,
const variant &v8, const variant &v9) {
        push_back(v1);
        push_back(v2);
        push_back(v3);
        push_back(v4);
        push_back(v5);
        push_back(v6);
        push_back(v7);
        push_back(v8);
        push_back(v9);
    }

    param_list(const variant &v1, const variant &v2, const variant &v3,
const variant &v4, const variant &v5, const variant &v6, const variant &v7,
const variant &v8, const variant &v9, const variant &v10) {
        push_back(v1);
        push_back(v2);
        push_back(v3);
        push_back(v4);
        push_back(v5);
        push_back(v6);
        push_back(v7);
        push_back(v8);
        push_back(v9);
        push_back(v10);
    }

    const variant &operator [] (int index) const {
        return at(index);
    }
};

void print(const param_list &params) {
    for(int i = 0; i < params.size(); i++) {
        switch (params[i].type()) {
            case t_char:
                cout << (char)params[i] << ", ";
                break;

            case t_short:
                cout << (short)params[i] << ", ";
                break;

            case t_long:
                cout << (long)params[i] << ", ";
                break;

            case t_int:
                cout << (int)params[i] << ", ";
                break;

            case t_byte:
                cout << (byte)params[i] << ", ";
                break;

            case t_word:
                cout << (word)params[i] << ", ";
                break;

            case t_dword:
                cout << (dword)params[i] << ", ";
                break;

            case t_uint:
                cout << (uint)params[i] << ", ";
                break;

            case t_string:
                cout << (string)params[i] << ", ";
                break;
        }
    }
}

int main() {
    char ch = 'A';
    print(param_list(1, ch, 3, "yo", 5));
    getchar();
    return 0;
}



May 23, 2003
Achilleas Margaritis wrote:
> I did not find them in D, and I could not find a relevant post in the
> newsgroups, but I assume I did not look hard enough. Anyway, if it has not
> been already proposed, here is my proposition to put variable argument
> declarations in D:

Consider grepping for "..." on the Phobos source. :>

-i.

May 24, 2003
I just recently posted some ideas almost exactly like these;  look for the thread "String formatting stuff", it's pretty deep down in the thread though.

Also see "typesafe varargs" thread.

Sean

"Achilleas Margaritis" <axilmar@in.gr> wrote in message news:balid5$2q65$1@digitaldaemon.com...
> I did not find them in D, and I could not find a relevant post in the newsgroups, but I assume I did not look hard enough. Anyway, if it has not been already proposed, here is my proposition to put variable argument declarations in D:
>
> A method that receives variable arguments (printf, for example), can be declared with a single parameter '...',as in C/C++. There is no need to declare another parameter first though. Before putting the arguments on
the
> stack, the compiler builds a table of argument information. Each entry in this table contains the type id of the entity passed on the stack and its address on the stack. Then, the programmer can use these information for accessing the objects in the stack in a safe manner. The syntax should be similar to accessing an array, only the name being predefined and
considered
> a property of the method. For example: