www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - no-preprocessor shortcoming? "function" macros at global scope

reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

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);=20
const HRESULT D3DERR_WRONGTEXTUREFORMAT=3DMAKE_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.
Jan 06 2005
next sibling parent reply Derek Parnell <derek psych.ward> writes:
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
Jan 06 2005
next sibling parent Derek Parnell <derek psych.ward> writes:
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
Jan 06 2005
prev sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
 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.
Jan 08 2005
prev sibling parent reply "Simon Buchan" <not a.valid.address.com> writes:
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
Jan 06 2005
next sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
 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.
Jan 08 2005
prev sibling parent reply Andy Friesen <andy ikagames.com> writes:
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
Jan 09 2005
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
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.
Jan 10 2005
parent "Simon Buchan" <buchan.home ihug.co.nz> writes:
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/
Jan 13 2005