Thread overview
no-preprocessor shortcoming? "function" macros at global scope
Jan 07, 2005
Derek Parnell
Jan 07, 2005
Derek Parnell
Jan 07, 2005
Simon Buchan
Jan 09, 2005
Andy Friesen
Jan 13, 2005
Simon Buchan
January 07, 2005
Here's something that doesn't seem to be very simple or possible in D - "function" macros which can be used at the global scope, unlike regular functions which can't be.

Take the following macros in COM and directx:

#define MAKE_HRESULT(sev,fac,code) \
    ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) )
#define _FACD3D  0x876
#define MAKE_D3DHRESULT( code )  MAKE_HRESULT( 1, _FACD3D, code )
#define D3DERR_WRONGTEXTUREFORMAT               MAKE_D3DHRESULT(2072)

This defines a constant, D3DERR_WRONGTEXTUREFORMAT with some complicated value that you'd normally not be able to come up with without help from the macros.

Converting to D, however, leaves you with:

HRESULT MAKE_HRESULT(int sev, int fac, int code) { return (sev << 31) | (fac << 16) | code; }
HRESULT MAKE_D3DHRESULT(int code) { return MAKE_HRESULT(1, _FACD3D, code);
const HRESULT D3DERR_WRONGTEXTUREFORMAT=MAKE_D3DHRESULT(2072);

Which, at global scope, of course gives you an error about it not being a constant expression.  You can't call functions at global scope.

I've tried doing this with a mixin, but it doesn't seem possible to define a variable with a mixed-in name within a mixin.

Of course, it would be possible to run the original C++ file through the preprocessor to get the big ugly error codes, but this still doesn't solve the overall problem.

January 07, 2005
On Thu, 6 Jan 2005 21:00:32 -0500, Jarrett Billingsley wrote:

> Here's something that doesn't seem to be very simple or possible in D - "function" macros which can be used at the global scope, unlike regular functions which can't be.
> 
> Take the following macros in COM and directx:
> 
> #define MAKE_HRESULT(sev,fac,code) \
>     ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) )
> #define _FACD3D  0x876
> #define MAKE_D3DHRESULT( code )  MAKE_HRESULT( 1, _FACD3D, code )
> #define D3DERR_WRONGTEXTUREFORMAT               MAKE_D3DHRESULT(2072)
> 
> This defines a constant, D3DERR_WRONGTEXTUREFORMAT with some complicated value that you'd normally not be able to come up with without help from the macros.
> 
> Converting to D, however, leaves you with:
> 
> HRESULT MAKE_HRESULT(int sev, int fac, int code) { return (sev << 31) | (fac << 16) | code; }
> HRESULT MAKE_D3DHRESULT(int code) { return MAKE_HRESULT(1, _FACD3D, code);
> const HRESULT D3DERR_WRONGTEXTUREFORMAT=MAKE_D3DHRESULT(2072);
> 
> Which, at global scope, of course gives you an error about it not being a constant expression.  You can't call functions at global scope.
> 
> I've tried doing this with a mixin, but it doesn't seem possible to define a variable with a mixed-in name within a mixin.
> 
> Of course, it would be possible to run the original C++ file through the preprocessor to get the big ugly error codes, but this still doesn't solve the overall problem.

Have you considered using the module constructor to create these 'constants' at runtime prior to main() getting control.

Otherwise you might try ...

import std.stdio;
alias uint HRESULT;
const uint _FACD3D = 0x876;
const HRESULT D3DERR_WRONGTEXTUREFORMAT = (1 << 31)|(_FACD3D << 16)|2072;

void main()
{
    writefln("0x%08x",  D3DERR_WRONGTEXTUREFORMAT);
}

-- 
Derek
Melbourne, Australia
7/01/2005 2:56:24 PM
January 07, 2005
On Fri, 7 Jan 2005 15:04:37 +1100, Derek Parnell wrote:

> On Thu, 6 Jan 2005 21:00:32 -0500, Jarrett Billingsley wrote:
> 
>> Here's something that doesn't seem to be very simple or possible in D - "function" macros which can be used at the global scope, unlike regular functions which can't be.
>> 
>> Take the following macros in COM and directx:
>> 
>> #define MAKE_HRESULT(sev,fac,code) \
>>     ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) )
>> #define _FACD3D  0x876
>> #define MAKE_D3DHRESULT( code )  MAKE_HRESULT( 1, _FACD3D, code )
>> #define D3DERR_WRONGTEXTUREFORMAT               MAKE_D3DHRESULT(2072)
>> 
>> This defines a constant, D3DERR_WRONGTEXTUREFORMAT with some complicated value that you'd normally not be able to come up with without help from the macros.
>> 
>> Converting to D, however, leaves you with:
>> 
>> HRESULT MAKE_HRESULT(int sev, int fac, int code) { return (sev << 31) | (fac << 16) | code; }
>> HRESULT MAKE_D3DHRESULT(int code) { return MAKE_HRESULT(1, _FACD3D, code);
>> const HRESULT D3DERR_WRONGTEXTUREFORMAT=MAKE_D3DHRESULT(2072);
>> 
>> Which, at global scope, of course gives you an error about it not being a constant expression.  You can't call functions at global scope.
>> 
>> I've tried doing this with a mixin, but it doesn't seem possible to define a variable with a mixed-in name within a mixin.
>> 
>> Of course, it would be possible to run the original C++ file through the preprocessor to get the big ugly error codes, but this still doesn't solve the overall problem.

Yes, it would be useful to be able to do this (or similar) ...

template mr(alias nam, alias sev, alias fac, alias code)
{
    const HRESULT nam = (sev << 31) | (fac << 16) | code;
}

mixin mr!(D3DERR_WRONGTEXTUREFORMAT, 1, _FACD3D, 2072);

-- 
Derek
Melbourne, Australia
7/01/2005 3:55:42 PM
January 07, 2005
On Thu, 6 Jan 2005 21:00:32 -0500, Jarrett Billingsley <kb3ctd2@yahoo.com> wrote:

> Here's something that doesn't seem to be very simple or possible in D - "function" macros which can be used at the global scope, unlike regular functions which can't be.
>
> Take the following macros in COM and directx:
>
> #define MAKE_HRESULT(sev,fac,code) \
>     ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) )
> #define _FACD3D  0x876
> #define MAKE_D3DHRESULT( code )  MAKE_HRESULT( 1, _FACD3D, code )
> #define D3DERR_WRONGTEXTUREFORMAT               MAKE_D3DHRESULT(2072)
>
> This defines a constant, D3DERR_WRONGTEXTUREFORMAT with some complicated value that you'd normally not be able to come up with without help from the macros.
>
> Converting to D, however, leaves you with:
>
> HRESULT MAKE_HRESULT(int sev, int fac, int code) { return (sev << 31) | (fac << 16) | code; }
> HRESULT MAKE_D3DHRESULT(int code) { return MAKE_HRESULT(1, _FACD3D, code);
> const HRESULT D3DERR_WRONGTEXTUREFORMAT=MAKE_D3DHRESULT(2072);
>
> Which, at global scope, of course gives you an error about it not being a constant expression.  You can't call functions at global scope.
>
> I've tried doing this with a mixin, but it doesn't seem possible to define a variable with a mixed-in name within a mixin.
>
> Of course, it would be possible to run the original C++ file through the preprocessor to get the big ugly error codes, but this still doesn't solve the overall problem.
>

from http://www.digitalmars.com/d/template.html :
"
Recursive Templates
Template features can be combined to produce some interesting effects, such as compile time evaluation of non-trivial functions. For example, a factorial template can be written:

template factorial(int n : 1)
{
    enum { factorial = 1 }
}

template factorial(int n)
{
    // Note . used to find global template rather than enum
    enum { factorial = n* .factorial!(n-1) }
}

void test()
{
    printf("%d\n", factorial!(4));// prints 24
}
"

Thus, your macros would be similar to:
template MAKE_HRESULT(ulong sev, ulong fac, ulong code) {
	return cast(HRESULT)(sev<<31 | (fac<<16 | code));
}

const int _FACD3D = 0x876;
alias MAKE_D3DHRESULT( code )    MAKE_HRESULT( 1, _FACD3D, code );
alias D3DERR_WRONGTEXTUREFORMAT  MAKE_D3DHRESULT(2072);

(not having anything to test it with makes it a bit difficult to check,
but this should be close)

--
"Yes, the american troops have advanced further. This will only
make it easier for us to defeat them" - Iraqi Information Minister
Muhammed Saeed al-Sahaf
January 08, 2005
> template MAKE_HRESULT(ulong sev, ulong fac, ulong code) {
> return cast(HRESULT)(sev<<31 | (fac<<16 | code));
> }
>
> const int _FACD3D = 0x876;
> alias MAKE_D3DHRESULT( code )    MAKE_HRESULT( 1, _FACD3D, code );
> alias D3DERR_WRONGTEXTUREFORMAT  MAKE_D3DHRESULT(2072);

There are a few problems with this snippet - one, the template isn't really a template (and doesn't have to be - it can just be a function).  Second, the "alias MAKE_D3DHRESULT" line is illegal - alias doesn't give you quite the same functionality as #define.  So, good effort, but I'm afraid it doesn't help much.


January 08, 2005
> Have you considered using the module constructor to create these 'constants' at runtime prior to main() getting control.

If I defined them in the module constructor, would they be at global scope? I would imagine not, but is there some kind of exception to the rule?

> const uint _FACD3D = 0x876;
> const HRESULT D3DERR_WRONGTEXTUREFORMAT = (1 << 31)|(_FACD3D << 16)|2072;

Well this certainly works, but it kind of loses the ease with which a macro would be used.


January 09, 2005
Simon Buchan wrote:

> Thus, your macros would be similar to:
> template MAKE_HRESULT(ulong sev, ulong fac, ulong code) {
>     return cast(HRESULT)(sev<<31 | (fac<<16 | code));
> }
> 
> const int _FACD3D = 0x876;
> alias MAKE_D3DHRESULT( code )    MAKE_HRESULT( 1, _FACD3D, code );
> alias D3DERR_WRONGTEXTUREFORMAT  MAKE_D3DHRESULT(2072);
> 
> (not having anything to test it with makes it a bit difficult to check,
> but this should be close)

Not quite, but the principle is quite sound.  This should work. (however I am also too lazy to test!)

    const ulong _FACD3D = 0x876;

    template MAKE_HRESULT(ulong sev, ulong fac, ulong code) {
        const ulong MAKE_HRESULT = (sev << 31) | (fac << 16) | code;
    }

    template MAKE_D3DHRESULT(ulong code) {
        const ulong MAKE_D3DHRESULT = MAKE_HRESULT!(1, _FACD3D, code);
    }

    const int D3DERR_WRONGTEXTUREFORMAT = MAKE_D3DHRESULT!(2072);

 -- andy
January 10, 2005
Well, that works great Andy!  Had to make a few changes to make it work with HRESULTs as they are technically ints and not ulongs, so here's the revised version:

template MAKE_HRESULT(uint sev, uint fac, uint code) {
         const HRESULT MAKE_HRESULT = cast(HRESULT)((sev << 31) | (fac <<
16) | code);
     }

     template MAKE_D3DHRESULT(uint code) {
         const HRESULT MAKE_D3DHRESULT = MAKE_HRESULT!(1, _FACD3D, code);
     }

     const HRESULT D3DERR_WRONGTEXTUREFORMAT = MAKE_D3DHRESULT!(2072);

I'm not real sure just HOW this works, but it does, so I'm not complaining. I dislike templates.


January 13, 2005
On Mon, 10 Jan 2005 18:52:37 -0500, Jarrett Billingsley <kb3ctd2@yahoo.com> wrote:

> Well, that works great Andy!  Had to make a few changes to make it work with
> HRESULTs as they are technically ints and not ulongs, so here's the revised
> version:
>
> template MAKE_HRESULT(uint sev, uint fac, uint code) {
>          const HRESULT MAKE_HRESULT = cast(HRESULT)((sev << 31) | (fac <<
> 16) | code);
>      }
>
>      template MAKE_D3DHRESULT(uint code) {
>          const HRESULT MAKE_D3DHRESULT = MAKE_HRESULT!(1, _FACD3D, code);
>      }
>
>      const HRESULT D3DERR_WRONGTEXTUREFORMAT = MAKE_D3DHRESULT!(2072);
>
> I'm not real sure just HOW this works, but it does, so I'm not complaining.
> I dislike templates.
>
>

The idea here is that templates are like functions for the compiler, not the
CPU (if that makes sense), just like macro's, only you can do funky recursive
branching stuff with proper, developed, consistant syntax. About the only thing
(Which is an incredibly ugly hack for namespacing, normally) that isn't covered
is token concatination.

-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/