I would like to convert this piece of PB's code to C/C++, but i don't know how to do it.
Could you help?
FUNCTION IUnknown_Release (BYVAL pthis AS DWORD PTR) AS DWORD
LOCAL DWRESULT AS DWORD
IF pthis THEN
CALL DWORD @@pthis[2] USING IUnknown_Release(pthis) TO DWRESULT
FUNCTION = DWRESULT
END IF
END FUNCTION
Hi Theo,
but not 2 bytes 8 bytes
pThis[0] offset 0 bytes
pThis[1] offset 4 bytes
pThis[2] offset 8 Bytes
You look at the test example
#COMPILER PBCC
#COMPILE EXE
#DIM ALL
FUNCTION PBMAIN () AS LONG
DIM a (0 TO 10) AS DWORD
a(0) = 1
a(1) = 2
a(2) = 3
LOCAL b AS DWORD PTR
LOCAL c AS DWORD PTR
b = VARPTR(a(0))
c = VARPTR(b)
testf(c)
WAITKEY$
END FUNCTION
SUB testf(BYVAL b AS DWORD PTR)
PRINT @@b[2]
END SUB
In my case, pthis, is a stream pointer got from CreateStreamOnHGlobal(MemBuffer, FALSE, pthis)
long IUnknown_Release (IN LPSTREAM* pthis) {
long NewReferenceCount = -1; // Means error
if (pthis) {
// what syntax should i use there?
}
return NewReferenceCount;
}
Hello Patrice Terrier
Since I unfortunately do not exercise more in C + +. I can only give you the tip you leave that translate from FreeBASIC with the C-emitter
The design is similar but the syntax is different!
Is that something that would make sense?
long IUnknown_Release (IN LPSTREAM* pthis) {
long NewReferenceCount = -1;
if (pthis) {
typedef long (__stdcall *zProc) (LPSTREAM*);
zProc hProc = (zProc) pthis[2];
if (hProc) { NewReferenceCount = hProc(pthis); }
}
return NewReferenceCount;
}
Yes could work this way.
Test it so
Use IStream_Release(pthis).
IStream_Release is a macro defined in objidl.h.
BTW the release method returns a DWORD, not a LONG.
Alternatively, you can use
(pthis)->lpVtbl -> Release(pthis)
Another variation:
typedef struct _fUnknown
{
HRESULT (*QueryInterface)(void* pvObject, REFIID riid, void **ppvObject);
ULONG (*AddRef)(void* pvObject);
ULONG (*Release)(void* pvObject);
} fUnknown,*pfUnk;
ULONG ReleaseObject(pfUnk*pthis)
{
if (pthis)
{
return pthis[0]->Release(pthis);
}
}
Ok, thanks to all of you.
But just for my learning, do you think that my syntax could work with this COM thing?
long IUnknown_Release (IN LPSTREAM* pthis) {
long NewReferenceCount = -1;
if (pthis) {
typedef long (__stdcall *zProc) (LPSTREAM*);
zProc hProc = (zProc) pthis[2];
if (hProc) { NewReferenceCount = hProc(pthis); }
}
return NewReferenceCount;
}
Charles
Does your code variation is intended to work on both 32 and 64-bit ?
...
I'd recommend just using CComPtr<IStream> and let it take care of the reference counting. Unless you're actually coding in plain C, you might as well take advantage of smart pointers for COM objects. I think this is where you can start getting into trouble if you simply do a line-by-line, verb-for-verb translation. Instead, consider what you're trying to do and see if there's a better way (easer to read, easier to maintain). When it comes to do with anything that has to deal with COM, I can guarantee you that ATL will make your coding appreciably easier.
Edit: Today is apparently not a good spelling day for me.
Form my learning, could you please answer to my question, if you understand the small code i posted, thanks?
So far, my verb for verb translation is the way i am learning, and the way i understand my own code ;)
Quote from: Patrice Terrier on April 07, 2013, 12:49:13 AM
Charles
Does your code variation is intended to work on both 32 and 64-bit ?
...
Certainly. The trick is to ensure that pointers are always expressed as pointers, and are never dereferenced to an integer or some other number.
Your example requires an extra dereferencing step before using accessing the function array. The equivalent to my previous example looks like this:
int ReleaseObjectA(void*pthis)
{
if (pthis) // valid COM object pointer
{
void**ppfunc=pthis; //address object body
void**pfunc=*ppfunc; //address function table ULONG (*Release)(void*pthis); //declare function
Release= pfunc[2]; //assign function address by index
return Release(pthis); //make call with object
}
}
Quote from: Charles Pegge on April 07, 2013, 10:53:31 PM
Certainly. The trick is to ensure that pointers are always expressed as pointers, and are never dereferenced to an integer or some other number.
Or if you do, use the UINT_PTR or uintptr_t polymorphic integrals that are guaranteed to be wide enough to store a pointer. For example, the following would work on a 32-bit system, but the compiler should (but Visual Studio 2010 and later won't necessarily) warn you that it's not safe for 64-bit:
UINT ptrValue = (UINT)&objValue; // Wrong
However, either of these would be fine:
UINT_PTR ptrValue = (UINT_PTR)&objValue; // C-style cast
uintptr_t ptrValue = reinterpret_cast<uintptr_t>(objValue); // Use reinterpret_cast to cast between pointers and integers
One other thing that might be of interest. Handles are 32 bits wide on x86 Windows and 64 bits wide on x64 Windows, as to be expected. But what about code like this:
HANDLE hFile = CreateFile(....);
DWORD dwParam = (DWORD)hFile;
hFile = (HANDLE)dwParam;
bResult = ReadFile(hFile, ....);
Again, that would work without a problem on 32-bit Windows but the compiler would complain just as it would in the code above. Except in this case, that code would actually work. Why? Because in Windows, all handles to kernel objects (files, sockets, threads, etc.) are guaranteed to only have their lower 32 bits be significant; on 64-bit Windows, the upper 32 bits of a handle will always be zero. Microsoft did that primarily to help with portability because a lot of applications out there did that kind of casting between handles and integers. That said, it's always best to cast them properly using UINT_PTR or uintptr_t, but I thought I'd throw out that bit of trivia.
Charles
The last syntax you posted works with GCC, but not with VS2010 C/C++
void**ppfunc=pthis; //address object body
Error: unable to use a "void *" type value to initialize an entity of "void **" type.
I am trying to find a good tutorial that would teach me how to use the obscure syntax of pointers in C/C++
???
Patrice,
One thing I don't see mentioned here is 64bit vc++ does not support inline asm.
If you need it you will have to use a 64bit assembler to create an object module and then link it into your app.
There may just not be a c++ alternative to do some of things PowerBASIC does especially for 64bit.
James
This would work with Visual C++:
ULONG IUnknown_Release(void *pObject)
{
if (pObject != NULL)
{
void **pvTable = (void **)(*(void **)pObject);
ULONG (__stdcall *_Release)(void *pThis) = (ULONG (__stdcall *)(void *))pvTable[2];
return _Release(pObject);
}
return 0;
}
Seriously though, look into using ATL and smart pointers. Using this kind of code in production software is just bad, both conceptually and practically when it comes to readability and long-term maintenance of the software. Looking behind the curtain is not necessary. By the way, that __stdcall there is necessary because COM methods are defined as STDMETHOD (which uses STDMETHODCALLTYPE which is defined as the stdcall calling convention).
Edit: And I should not post stuff like this before I've had my coffee. I threw in the check for NULL there just to be on the safe side.
James,
Yep, unfortunatly i also have to convert a few lines of ASM code to 64-bit as well >:(
like this one (that could have been written by Charles himself)
FUNCTION Rgb2Gray (BYVAL RGBValue AS DWORD) AS DWORD
#REGISTER NONE
!XOR ESI, ESI
!XOR EDX, EDX
!MOV ECX, RGBValue
' Red
!MOV EAX, 19595
!MOV DL, CL
!MUL EDX
!ADD ESI, EAX
' Green
!MOV EAX, 38470
!SHR ECX, 8
!MOV DL, CL
!MUL EDX
!ADD ESI, EAX
' Blue
!MOV EAX, 7471
!SHR ECX, 8
!MOV DL, CL
!MUL EDX
!ADD ESI, EAX
!SHR ESI, 16
!MOV EDX, ESI
' Put Gray Value to a DWORD (0GGG)
!XOR EAX, EAX
!OR AL, DL
!SHL EAX, 8
!OR AL, DL
!SHL EAX, 8
!OR AL, DL
!MOV FUNCTION, EAX
END FUNCTION
Quote from: James C. Fuller on April 08, 2013, 04:22:08 PM
There may just not be a c++ alternative to do some of things PowerBASIC does especially for 64bit.
A quick search turned up the formula for converting RGB to grayscale as (0.299 * R + 0.587 * G + 0.114 * B)
Admittedly, I'd expect the assembler version to be faster than what's generated by a C++ compiler, but then the necessity of that would depend on an actual performance analysis of the code. If usage metrics show that the function is called infrequently over the lifetime of an application, that's not necessarily a good optimization choice.
Yes the assembler version was a replacement for the previous one, that was... too slow.
' Red? = (RGBValue AND &H000000FF???) * .299
' SHIFT RIGHT colorRGB???, 8
' Green? = (RGBValue AND &H000000FF???) * .587
' SHIFT RIGHT colorRGB???, 8
' Blue? = (RGBValue AND &H000000FF???) * .114
' Gray? = Red? + Green? + Blue?
' FUNCTION = RGB(Gray?, Gray?, Gray?)
Patrice,
You could create an overlay structure to access the color bytes directly, rather then bit shifting the RGB.
On the subject of void**, will VC10 let you use a cast, to override its rules?
void**ppfunc= (void**) pthis;
You can do all kinds of dangerous stuff with C-style casting to void pointers that will make the compiler stop complaining. Enough rope to hang yourself and whatnot, you know? In terms of getting to the class vtable, keep in mind that the first member of the object is the vpointer (a pointer to the vtable, not the vtable itself) so you actually have to derference that. So you end up with funky looking stuff like what I posted up there:
void **pvTable = (void **)(*(void **)pObject);
In truth though, this code is all kinds of bad. First and foremost, it makes the assumption that in the layout of the object, the first member is the vpointer, but that's not guaranteed. While most compilers do place the vpointer at the beginning of the object, it also would be perfectly legitimate for the compiler to put it at the end. Technically, the standard doesn't define how the method dispatch should be implemented, so there's no requirement that the vtable even be an array of pointers. Bottom line, code like this is non-portable and that's why I say that it shouldn't be used.
This technique is reliable for COM IUnkmown /IDispatch interfaces. But I agree, Mike, that it does not apply to other kinds of objects. For instance, Objects with multiple inheritance may use several function table pointers embedded in its structure. But objects which are not exported or imported, do not require any such pointers since the compiler can generate direct links for method calls. (I do this in OxygenBasic)
You're right, I was thinking in more general terms, but COM defines the layout of the vtable in the object as part of the standard. It still don't think it's a good idea to write code like this generally, but I shouldn't have characterized it as "dangerous" when it specifically comes to COM objects.
Charles--
QuoteYou could create an overlay structure to access the color bytes directly, rather then bit shifting the RGB.
Could you ellaborate on that, what kind of overlay structure?
...
Patrice,
I have very little 64bit assembler knowledge but maybe this link might help you:
http://www.godevtool.com/GoasmHelp/64bits.htm#adapt
James
Quote from: Patrice Terrier on April 09, 2013, 02:13:16 PM
Charles--
QuoteYou could create an overlay structure to access the color bytes directly, rather then bit shifting the RGB.
Could you ellaborate on that, what kind of overlay structure?
...
Just using typedefs and pointers. I think this will give you good speed, doing all the pixels in one function helps.
#include <stdlib.h>
#include <stdio.h>
typedef struct _RGB
{
char red,green,blue;
} tRGB, *pRGB;
typedef unsigned long tGRAY,*pGRAY;
int main()
{
int width=640, height=480;
int e=width*height;
pRGB ColorImage = malloc(e*sizeof(tRGB));
pGRAY GrayImage = malloc(e*sizeof(tGRAY));
//...
int i;
pRGB color = ColorImage;
pGRAY gray = GrayImage;
//
for (i=0;i<e;i++)
{
//RGB to grayscale as (0.299 * R + 0.587 * G + 0.114 * B)
*gray=(0.299*color->red)+
(0.587*color->green)+
(0.114*color->blue);
color++;
gray++;
}
//...
free(ColorImage);
free(GrayImage);
}
Charles
I'm by no means a graphics guy, and don't know about the context in which that function is being used, but if the end-goal is the conversion of an image from color to grayscale, wouldn't the use of a ColorMatrix in GDI+ be the simplest (and fastest) approach? I'd have to presume that all of the low-level optimization, etc. that would need to be done has already been done there. Just a thought.
Much of this is an exercise in C comprehension for Patrice and myself. We both need to understand C from the ground level.
Mike, do you recall we had a conversation about first-class functions and closures, on the thinBasic forum. I am still a little mystified by closures, so perhaps we could start a new topic on functional programming.
Mike--
It does not serve the same purpose, in GDImage it is used to create an image with variable opacity where the 256 level of gray are being used to assign a specific alpha channel to each pixel of an image, something totaly different that converting a whole image to gray. Indeed it works more like what OpenGL does when using GL_BLEND.
pBits = bm.bmBits
FOR y = 0 TO bm.bmHeight - 1
FOR x = 0 TO (bm.bmWidth - 1)
@pBits[3] = Rgb2Gray(RGB(@pBits[2],@pBits[1],@pBits[0]))
pBits = pBits + 4
NEXT
NEXTAs you can see from the above code only the alpha channel is changed, the other RGB components are left unchanged.
And you can understand now, why the Rgb2Gray must be as fast as possible.
This is something totaly different than this:
LONG_PTR UseGrayMatrix(IN LONG_PTR imgAttr) {
ColorMatrix c, g;
// Create the ImageAttributes object
if (imgAttr == 0) { GdipCreateImageAttributes(imgAttr); }
// Fill the color matrix
// Red Green Blue Alpha W
c.m[0][0] = 0.3f ; c.m[1][0] = 0.3f ; c.m[2][0] = 0.3f ; c.m[3][0] = 0.0f ; c.m[4][0] = 0.0f; // Red
c.m[0][1] = 0.59f ; c.m[1][1] = 0.59f ; c.m[2][1] = 0.59f ; c.m[3][1] = 0.0f ; c.m[4][1] = 0.0f; // Green
c.m[0][2] = 0.11f ; c.m[1][2] = 0.11f ; c.m[2][2] = 0.11f ; c.m[3][2] = 0.0f ; c.m[4][2] = 0.0f; // Blue
c.m[0][3] = 0.0f ; c.m[1][3] = 0.0f ; c.m[2][3] = 0.0f ; c.m[3][3] = 1.0f ; c.m[4][3] = 0.0f; // Alpha
c.m[0][4] = 0.0f ; c.m[1][4] = 0.0f ; c.m[2][4] = 0.0f ; c.m[3][4] = 0.0f ; c.m[4][4] = 1.0f; // W
// And set its color matrix
GdipSetImageAttributesColorMatrix(imgAttr, ColorAdjustTypeDefault, TRUE, c, g, ColorMatrixFlagsDefault);
return imgAttr;
}
Charles--
QuoteMuch of this is an exercise in C comprehension for Patrice and myself. We both need to understand C from the ground level.
So true, thank you :)
Quote from: Charles Pegge on April 10, 2013, 07:09:12 AM
Mike, do you recall we had a conversation about first-class functions and closures, on the thinBasic forum. I am still a little mystified by closures, so perhaps we could start a new topic on functional programming.
Sure, but in the context of C++ though you kind of end out far into the weeds. C++11 introduced lambdas (anonymous functions) and closures that look like this:
int foo = 1;
for_each(v.begin(); v.end(); [&foo] (int bar) -> int
{
return bar * foo++;
});
;
It does make things more convenient (for example, creating class delegates) but, to me, this just looks ugly. Other languages have a much cleaner implementation (such as C# and JavaScript). But perhaps it's just because I'm an old C programmer from days gone by and these are the new kids partying on my front lawn.
Edit: I'm just typing this off the top of my head, so hopefully I got the syntax right there (and it's somewhat complicated by the fact that not all of the current C++ compilers out there completely support the standard). Your mileage may vary, as they say.
I hear you Mike.
MinGW 4.8 has all of it I think.
This is one of the distro's I am using: http://nuwen.net/mingw.html
An MS employee's personal site.
Even though I am still early on in my c++ education I really like some of the new c11 stuff.
some of my favorites:
direct vector assigment, reference based loops and of course "auto"
With the nuwen distro this will compile with just this simple command line:
g++ v4.cpp -ov4.exe
James
#include <iostream>
#include <vector>
using namespace std;
int main (void);
int main ()
{
vector<string> s = {"one","two","three","four","five"};
cout << "s contains" << endl;
for(auto it = s.begin(); it != s.end(); ++it)
{
cout << " " << *it;
}
cout << endl;
cout << "in reverse" << endl;
for(auto rit = s.rbegin(); rit != s.rend(); ++rit)
{
cout <<" " << *rit;
}
cout << endl;
cout << "range-based for loop" << endl;
for ( auto it : s)
{
cout << " " << it;
}
cout << endl;
return 0;
}