I'll send 4 patches here for review. Once the comments stop I'll send them to patches. Do I send patches to .h files or the .idl is enough?
PATCH 1 uuidof.diff - enable use of some ms extensions
- include/wine/uuidof.h (new) Lets GCC compiler use the uuidof MSVC++ extension used in ATL. See comments inside the file
- include wine/pretty_com.h (new) Lets any C++ compiler use the __decelspec( property () ) MSVC++ extension. See comments inside the file
- tools/pretty_com.pl A Perl script that changes __decelspec( property () ) declarations to the proper macros from pretty_com.h. see comments inside the file
- include/unknwn.idl (updated) Adds a C++ only extension to the IUnknown Interface. This is to let ATL compile (use of uuidof.h)
Index: include/unknwn.idl =================================================================== RCS file: /home/wine/wine/include/unknwn.idl,v retrieving revision 1.4 diff -u -r1.4 unknwn.idl --- include/unknwn.idl 12 Apr 2003 00:09:14 -0000 1.4 +++ include/unknwn.idl 10 Mar 2004 08:36:47 -0000 @@ -30,7 +30,6 @@ cpp_quote("#endif")
/* Interfaces */ - [ local, object, @@ -46,7 +45,58 @@ [out, iid_is(riid)] void **ppvObject); ULONG AddRef(); ULONG Release(); + +cpp_quote("/*This is so we can add the Template QueryInterface for use by ATL*/") +cpp_quote("/*do a -D_WINE_NO_UUIDOF_ if your cplusplus compiler breaks on it*/") +cpp_quote("#if defined(__cplusplus) && !defined(_WINE_NO_UUIDOF_)") +cpp_quote("") +cpp_quote("#include <wine/uuidof.h>") +cpp_quote("#ifndef __IUnknown_INTERFACE_DEFINED__") +cpp_quote("#define __IUnknown_INTERFACE_DEFINED__") +cpp_quote("") +cpp_quote("DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46);") +cpp_quote("") +cpp_quote("extern "C++" {") +cpp_quote("") +cpp_quote("#ifdef ICOM_USE_COM_INTERFACE_ATTRIBUTE") +cpp_quote("struct __attribute__((com_interface)) IUnknown") +cpp_quote("#else") +cpp_quote("struct IUnknown") +cpp_quote("#endif") +cpp_quote("{") +cpp_quote(" virtual HRESULT STDMETHODCALLTYPE QueryInterface(") +cpp_quote(" REFIID riid,") +cpp_quote(" void** ppvObject) = 0;") +cpp_quote("") +cpp_quote(" virtual ULONG STDMETHODCALLTYPE AddRef(") +cpp_quote(" ) = 0;") +cpp_quote("") +cpp_quote(" virtual ULONG STDMETHODCALLTYPE Release(") +cpp_quote(" ) = 0;") +cpp_quote("") +cpp_quote(" template <class Q>") +cpp_quote(" HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp)") +cpp_quote(" {") +cpp_quote(" return QueryInterface(__uuidof(Q), (void**)pp);") +cpp_quote(" }") +cpp_quote("};") +cpp_quote("") +cpp_quote("} // extern "C++"") +cpp_quote("") +cpp_quote("#define IUnknown_METHODS \") +cpp_quote(" ICOM_MSVTABLE_COMPAT_FIELDS \") +cpp_quote(" /*** IUnknown methods ***/ \") +cpp_quote(" STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE; \") +cpp_quote(" STDMETHOD_(ULONG,AddRef)(THIS) PURE; \") +cpp_quote(" STDMETHOD_(ULONG,Release)(THIS) PURE;") +cpp_quote("") +cpp_quote("#endif //__IUnknown_INTERFACE_DEFINED__") +cpp_quote("#else /* defined(__cplusplus) && !defined(_WINE_NO_UUIDOF_) */") } + +cpp_quote("#endif /* defined(__cplusplus) && !defined(_WINE_NO_UUIDOF_) */") +cpp_quote("") +cpp_quote("")
[ object,
--- /dev/null 1970-01-01 02:00:00.000000000 +0200 +++ include/wine/pretty_com.h 2004-02-25 21:21:18.000000000 +0200 @@ -0,0 +1,218 @@ +/* + * Add support for Microsoft extention + * __declspec( property (get= xxxx,put = yyyy) ) + * + * Copyright 2004 Boaz Harrosh boaz@ElectroZaur.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __pretty_com_h__ +#define __pretty_com_h__ + +#if !defined( __cplusplus ) +# error Use this file only for C++ compilation +#endif + +/* +explanation: + the ms-extention __declspec(property()) lets you associate a ?fake? variable name to + one or a pair of class member functions. So function calls will be made where in + code a variable assignment or access is made. This makes C++ syntax look more + like script or 4th generation language. + +For example: + class HasProperty{ + public: + __declspec( property ( get=GetFoo ,put=PutFoo ) ) + int Foo ; + + int GetFoo() ; + void PutFoo(int newVal) ; + } +will let you use code like: + HasProperty hp ; + int i = hp.Foo ; + hp.Foo = i * 17 ; + +In effect Line 2 above invokes GetFoo() (R-Value) +and Line 3 will invoke PutFoo(..) (L-Value) +Foo has no allocation in memory and HasProperty does not grow in any way +by the presence of Foo. + +Below code will let such code usage to be used by any standard C++ compiler. + __declspec declarations must change but source code can stay the same. + Same new declarations can than be used by both MSVC or other compilers. + +Use: + ALL_P( type, PropertyName ) + GET_P( type, PropertyName ) + PUT_P( type, PropertyName ) +if the Foo name will have a GetFoo/PutFoo corresponding members. + +Use: + ALL_PROP( type, PropertyName, GetFunction ,PutFunction ) + GET_PROP( type, PropertyName, GetFunction ) + PUT_PROP( type, PropertyName, PutFunction ) +if you need specific control on associated member function names + +Don't forget to use DECLARE_PROP_CLASS( classname ) anywhere in +the class before the first property. + +Note: + the pretty_com.pl program was written for code generated by the MSVC + #import directive. It will assume the simple GetFoo/PutFoo naming. + Feel free to submit fixes for a more general program +*/ + +#if defined( _GNUC_ ) +// original offsetof will warn on GCC so below will turn this warning off +// 128 is to avoid alignment fix ups. +# undef offsetof +# define offsetof(s,m) ( ((size_t)&( ((s*)128)->m )) - 128 ) +#endif + +#ifdef _MSC_EXTENSIONS +/*compiled under msvc with extentions turned on (default for msvc)*/ + +#define DECLARE_PROP_CLASS( aclass ) + +#define ALL_P( type ,name )\ +__declspec(property(get=Get##name,put=Put##name))\ +type name ; + +#define GET_P( type ,name )\ +__declspec(property(get=Get##name))\ +type name ; + +#define PUT_P( type ,name )\ +__declspec(property(put=Put##name))\ +type name ; + +#define ALL_PROP( type ,name ,get_f ,put_f)\ +__declspec(property(get=get_f,put=put_f))\ +type name ; + +#define GET_PROP( type ,name ,get_f)\ +__declspec(property(get=get_f))\ +type name ; + +#define PUT_PROP( type ,name ,put_f)\ +__declspec(property(put=put_f))\ +type name ; + +#else //_MSC_EXTENSIONS +/*compiled with other c++ (not msvc) compiler, or without extentions turned on */ + +#define DECLARE_PROP_CLASS( aclass ) typedef aclass __this_class__ ; + +#define ALL_P( type ,name )\ + class prop_func_##name{public: \ + __this_class__& MyClass() \ + { char* p = (char*)this - offsetof(__this_class__,name) ; \ + return *((__this_class__*)p) ; \ + } \ + operator type(){return MyClass().Get##name();} \ + type operator =(type val){MyClass().Put##name(val);return val;} \ + } name ; + +#define GET_P( type ,name )\ + class prop_func_##name{public: \ + __this_class__& MyClass() \ + { char* p = (char*)this - offsetof(__this_class__,name) ; \ + return *((__this_class__*)p) ; \ + } \ + operator type(){return MyClass().Get##name();} \ + } name ; + +#define PUT_P( type ,name )\ + class prop_func_##name{public: \ + __this_class__& MyClass() \ + { char* p = (char*)this - offsetof(__this_class__,name) ; \ + return *((__this_class__*)p) ; \ + } \ + type operator =(type val){MyClass().Put##name(val);return val;} \ + } name ; + +#define ALL_PROP( type ,name ,get_f ,put_f)\ + class prop_func_##name{public: \ + __this_class__& MyClass() \ + { char* p = (char*)this - offsetof(__this_class__,name) ; \ + return *((__this_class__*)p) ; \ + } \ + operator type(){return MyClass().get_f();} \ + type operator =(type val){MyClass().put_f(val);return val;} \ + } name ; + +#define GET_PROP( type ,name ,get_f)\ + class prop_func_##name{public: \ + __this_class__& MyClass() \ + { char* p = (char*)this - offsetof(__this_class__,name) ; \ + return *((__this_class__*)p) ; \ + } \ + operator type(){return MyClass().get_f();} \ + } name ; + +#define PUT_PROP( type ,name ,put_f)\ + class prop_func_##name{public: \ + __this_class__& MyClass() \ + { char* p = (char*)this - offsetof(__this_class__,name) ; \ + return *((__this_class__*)p) ; \ + } \ + type operator =(type val){MyClass().put_f(val);return val;} \ + } name ; + +#endif //_MSC_EXTENSIONS + +/* +Implementation notes: +1) + the trick is in the MyClass() member of each property. it will calculate + the containing class address from its own (this) address. + Since properties are size less - [Q] what will be the offset of such a property? + + Yes you guest right! always 0. where ever it will be declared inside the class and + as many as there are. So in effect above can Just be: "return this;". + and the all offset-of warnings can be avoided. Now, not that I think that any compiler + on any imaginable machine will ever produce a different code. Its Just that I do + Imagine a long class hierarchy that have special properties with none zero size. + So I'll leave the code as is. It is clearer this way and the optimizer will always + remove the -0 operation. So it is only for us programmers. + +2) + Originally I have tried to keep the __declspec( property (get=xxx,put=yyy) ) syntax + but since I absolutely needed a macro, and macros can not be overloaded + the only close syntax I could achieve was + __declspec( property ((get=xxx,put=yyy)) ) with double brackets on the inside. And I do + need the type inside the macro - something like: + __declspec( property<type_name> ((get=xxx,put=yyy)) ) + Since code had to be changed any way (and it is more complicated) I did it as above. + +[Q] Student exercise: + Original ms-extension will also let you define an Array of properties where the index + of the array is supplied as parameter to the function member. + +Example: + __declspec( property (get=GetArray) ) + double Array[117] ; + double GetArray(size_t index) ; + ... + double f = Foo.Array[17] ; +How would you extend the above to support such syntax? +(When I ever see such code used I'll supply a fix) +*/ + +#endif //__pretty_com_h__ --- /dev/null 1970-01-01 02:00:00.000000000 +0200 +++ include/wine/uuidof.h 2004-02-09 14:55:24.000000000 +0200 @@ -0,0 +1,145 @@ +/* + * Description: + * + * __decelspec(uuid()) and __uuidof are Microsoft extensions to the C++ compiler that lets you attach + * a GUID (128 bit number) to a class/struct and than retrieve it by the use of __uuidof + * + * below code tries to do that. + * any code using __uuidof does not change and expected run time is identical to MSVC. + * declareations of the uuid has to change. There are two alternatives. + * 1) Leave __declspec(uuid(...)) has is. Which is #defined to nothing in wine. Than + collect all uuid classes used in your project into one .CPP file + and use the MAKE_UUIDOF??? macros to define them. + (other wise the linker will complain) + * 2) Use the pretty_com.pl script to change all __declspec(uuid(...)) to the __decelspec_uuid2(...,InterfaceName) + macro. That will do a similar thing to the above but can survive in an header. + (or change these declarations manually) + + * Copyright (C) the Wine project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef WINE_UUIDOF_H_ +#define WINE_UUIDOF_H_ + +#if defined( __cplusplus ) && \ + !defined ( _WINE_NO_UUIDOF_ ) && \ + !defined (_MSC_VER) && \ + defined (__GNUC__) + +extern "C++" { + +/* + at first I tried to use macros but ATL, who all this work was for, + uses __uuidof in template parametrization, which forces me to use template too. + Any way with templates the solution becomes trivial, much simpler + than macros. (the ones I had) please send comments to boaz@hishome.net + I would love to talk about it. +*/ +template< class T> +struct hold_uuidof +{ + static GUID __IID ; +}; + +} /*extern C++*/ + +/* + this is a nice one. In MSVC++ u can do: + __uuidof(Foo) __uuidof(pointer2Foo) __uuidof(refranceofFoo) + and __uuidof(classAFoo). The GCC typeof() will take care of that + (get it: use one's extension to implement another) +*/ +#define __uuidof( Q ) hold_uuidof< typeof(Q) >::__IID + +/* + use this macro to assosiate a guid to a class/struct. +*/ +#define MAKE_UUIDOF_ID( class ,IID ) \ + template<> \ + GUID hold_uuidof<class>::__IID = IID; + +/* + This one is the same as above but: + presupposes an IID_Interface was already defined +*/ +#define MAKE_UUIDOF( class ) \ + MAKE_UUIDOF_ID(class, IID_##class) + + +/* + [Q] why add more macros to the mess? + + uuid uses a registry notation of the form: + "12345678-1234-1234-1234-123456789ABC" + + the classic DEFINE_GUID will use +(IID_NAME ,0x12345678, 0x1234, 0x1234, 0x12,0x34, 0x12,0x34,0x56,0x78,0x9A,0xBC); + + it was easier for me to write a pearl script (see pretty_com.pl) and is more readable + if it was of the form: + ( 0x12345678, 0x1234, 0x1234, 0x1234, 0x123456789ABCLL, class ) + + to add to all the mess Microsoft MIDL compiler will use 2 other macros + DECLSPEC_UUID(x) MIDL_INTERFACE(x) which are the same only the later imposes + a struct definition. They expect a registry notation as uuid above. + Wine steers away from these macros by defining them to nothing. + + pretty_com.pl will translate all __declspec( uuid(X) ) instances to __declspec_uuid2(x,InterfaceName) + defined below. + + An alternative to __declspec_uuid2 and pretty_com.pl can be to use below MAKE_XXX macros + + What is MinGW-gcc position on all this? + +*/ + +#include <pshpack1.h> +struct betterguid{ + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned short Data4; + unsigned short Data5; + unsigned long Data6; +} ; +#include <poppack.h> + +#define INSTANTIATE_BETTER_GUID(ld1 ,sd2 ,sd3 ,sd4 ,ll5) \ + { ld1,sd2,sd3,sd4,ll5>>32, ll5&0xffffffff } + +#define DEFINE_IID( ld1 ,sd2 ,sd3 ,sd4 ,ll5 ,class ) \ +__attribute__((weak)) betterguid a##class = INSTANTIATE_BETTER_GUID(ld1 ,sd2 ,sd3 ,sd4 ,ll5) ;\ +EXTERN_C const GUID IID_##class = *((GUID*)&a##class) ; + +#define DEFINE_CLSID( ld1 ,sd2 ,sd3 ,sd4 ,sd5 ,ld6 ,class ) \ + __attribute__((weak)) betterguid a##class = INSTANTIATE_BETTER_GUID(ld1 ,sd2 ,sd3 ,sd4 ,ll5) ;\ +EXTERN_C const GUID CLSID_##class = *((GUID*)&a##class) ; + +// make uuidof and IID_ in one shot +#define MAKE_IID( ld1 ,sd2 ,sd3 ,sd4 ,ll5 ,class ) \ +DEFINE_IID( ld1 ,sd2 ,sd3 ,sd4 ,ll5 ,class ) \ +MAKE_UUIDOF(class) ; + +#define __declspec_uuid2(ld1 ,sd2 ,sd3 ,sd4 ,ll5 ,class) + +// below is used by pretty_com.pl +#define __info_uuid( uuid_string ) + + +#endif /*defined( __cplusplus ) && !defined ( _WINE_NO_UUIDOF_ ) && !defined (_MSC_VER)*/ + +#endif /*ndef WINE_UUIDOF_H_*/ --- /dev/null 1970-01-01 02:00:00.000000000 +0200 +++ tools/pretty_com.pl 2004-02-10 15:47:21.000000000 +0200 @@ -0,0 +1,182 @@ +#!/usr/bin/perl -w + +# pretty_com.pl will try to crudely parse Microsoft C++ COM header files and fix them for compilation +# under GCC and other standard C++ compilers. + +# pretty_com.pl will change any occurrence of _declspec( property (get=,put=) ) +# to the proper macro invocation. An #include directive will be added to pretty_com.h where +# these macros are defined. +# +# pretty_com.pl will also take care of __declspec( uuid(?hhh-hhh?)). It will distinguish between 2 cases: +# 1) when it is a class declaration. - +# It will than change it to a __declspec_uuid2( 0xhhh,0xhhh ) macro compatible with uuidof.h header file. +# 2) when it is just a forward declaration. - +# It will change it to a __info_uuid(?hhh-hhh?) which is declared to nothing. +# +# (see ?pretty_com.h? and ?uuidof.h? for details.) + +# Usage: all arguments are filenames to be processed. +# Contents of "filename" will change. The old file will be saved in "filename.old" +# any file parsed is reported to stdout with a comment "changed" or "nothing to do" +# in the second case no .old file was created. +# [example:]> perl -w pretty_com.pl header1 [header2] .. +# + +# FIXME: Below code is made to cope with code generated by the #import directive of +# MSVC. I am afraid my scripting skills are very limited and a more robust approach +# should be used to cope with all variations of the declspec(property()) syntax. +# mainly it will rely on exactly 1 new-line between the __declspec(property()) and the variable declaration. +# Please some one with better scripting skills, check the code and remove this FIXME +# Free Life boaz@electrozaur.com + + +use warnings; + +$class_decl = "" ; +$newuuid = "" ; + +# input is in $class_decl - it is the string from "struct" | "class" all the way up to the "{" or ";" +# output is in $newuuid - comma delimited and 0x appended to each hive. +# Example: +# __declspec(uuid("12345678-1234-1234-1234-123456789ABC") +# changes to +# 0x12345678,0x1234,0x1234,0x1234,0x123456789ABCLL +sub ExtractUuid +{ + $newuuid = $class_decl ; + + if( $newuuid =~ s/^.*__declspec[\s]*([\s]*uuid[\s]*([\s]*"(.*)"[\s]*)[\s]*)[\s]*(\w*)[\s]*.*/0x$1LL,$2/ ) + { + $newuuid =~ s/-/,0x/g ; + $newuuid =~ s/\n//g ; + $newuuid =~ s/{//g ; + $newuuid =~ s/;//g ; +# $newuuid = "0x" . $newuuid ; + print $newuuid . "\n"; + } +} + +# input - reference to Input-File as first param, $_ contains the first line containing the keyword struct|class +# output - +# return: +# true - A class declaration was found +# false - A forward declaration ending with ";" +# $class_decl: +# will contain all the class declaration from the key word class|struct up to including +# the "{" or ";" +# InFile: is positioned pass the "{" | ";" + +sub readUntilB +{ + $InFile = shift ; + $read_lines = "" ; + do { + if ( $_ =~ /{/ ) { + $pos_of_B = index($_ ,"{") ; + $offset_from_end = $pos_of_B - length($_) + 1; # we need a negative number + seek( $InFile ,$offset_from_end ,1 ) ; + $read_lines .= substr $_ ,0 , ($pos_of_B) + 1; + $class_decl = $read_lines ; + return 1 ; + } elsif ( $_ =~ /;/ ) { + $class_decl = $read_lines . $_ ; + return 0 ; + } else { + $read_lines .= $_ ; + } + }while ( <$InFile> ) +} + +foreach $InFileName (@ARGV) +{ + open( InFile ,$InFileName) or die "Cant open $InFileName !"; + print "Proccessing $InFileName - " ; + + $LastInclude = 0 ; + $curLine = 0 ; + $Has_property = 0 ; + $Include_pretty_com = "#include <pretty_com.h>" ; + + #first pass see if anything to do and find a good place for the #include statment + # which is after any include but before any uuid or property + while( <InFile> ) + { + $curLine += 1 ; + if( ! $Has_property ) { + $LastInclude = $curLine if(/#include/) ; + } + $Has_property = 1 if( (/__declspec[\s]*([\s]*property/) || (/__declspec[\s]*([\s]*uuid/)) ; + } + + if( $Has_property ) + { + $OutFileName = "$InFileName.new" ; + open( OutFile,">$OutFileName") or die "Cant open $OutFileName !" ; + + $curLine = 0 ; + seek( InFile ,0 ,0 ) ; + while( <InFile> ) + { + # if we find class or struct fix that up + if( ($_ =~ /struct/) || ($_ =~ /class/) ) { + if( readUntilB(*InFile) ) { + ExtractUuid() ; + print OutFile "__declspec_uuid2" . "(" . $newuuid . ")\n" ; + $class_decl =~ s/__declspec[\s]*([\s]*uuid[\s]*([\s]*"(.*)"[\s]*)[\s]*)//; + @decl_ = split ':' , $class_decl ; + $BaseClassname = pop @decl_ ; + $Classpart = pop @decl_ ; + if( !$Classpart ) { + $Classpart = substr($BaseClassname ,0 ,length($BaseClassname) - 1); + } + @ClassNameToks = split ' ', $Classpart ; + $Classname = pop @ClassNameToks ; + + print OutFile $class_decl ; + print OutFile "\nDECLARE_PROP_CLASS($Classname)" ; + } else { + ExtractUuid() ; + $class_decl =~ s/__declspec[\s]*([\s]*uuid((.*)))/__info_uuid($1)/; + print OutFile $class_decl ; + } + # if we find __declspec(property fix that up + } elsif ($_ =~ /__declspec(property/) { + chomp; + $decl = $_; + chop ($nextline = <InFile>); + $nextline =~ s/;//g; + @words = split ' ', $nextline; + $id = pop @words; + $type = join ' ', @words; + if (($decl =~ /get=/) and ($decl =~ /put=/)) { + print OutFile ("\tALL_P( $type\t, $id );\n"); + } + elsif ($decl =~ /get=/) { + print OutFile ("\tGET_P( $type\t, $id );\n"); + } + elsif ($decl =~ /put=/) { + print OutFile ("\tPUT_P( $type\t, $id );\n"); + } + # default copy lines to new file - put include in place + } else { + print OutFile $_ ; + $curLine += 1 ; + if( $curLine == $LastInclude ){ + print OutFile $Include_pretty_com ; + } + } + } + close(InFile) ; + close(OutFile) ; + + # until now Output was done to FileName.new rename orignal file to .old and make new one the file +# rename( $InFileName ,"$InFileName.old" ) or print "Cant rename $InFileName to .old\n" ; +# rename( $OutFileName ,$InFileName ) or print "Cant rename $OutFileName to $InFileName\n" ; + + print "changed!\n" ; + } + else { + print "nothing to do\n"; + } + +}