www.digitalmars.com         C & C++   DMDScript  

D - variable argument lists

reply "Achilleas Margaritis" <axilmar in.gr> writes:
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
next sibling parent Ilya Minkov <midiclub 8ung.at> writes:
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 23 2003
prev sibling parent "Sean L. Palmer" <palmer.sean verizon.net> writes:
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:
May 24 2003