r3wp [groups: 83 posts: 189283]
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

World: r3wp

[!REBOL3 Extensions] REBOL 3 Extensions discussions

Maxim
10-Nov-2010
[1719x2]
and replace:
#define RL_PRINT(a,b)               RL->print(a,b)

with:
#define RL_PRINT(a,b)               RL->print(a,__VA_ARGS__)

can't test it right now, but that should work.
the function itself is defined with variable arguments, its just 
the #define which isn't ... the above redefines it so it matches 
the function declaration.
Oldes
10-Nov-2010
[1721]
__VA_ARGS__ can only appear in the expansion of a C99 variadic macro
Never mind... I really have to do real work now :)
Maxim
10-Nov-2010
[1722]
what?  I'm using it my own gcc compiled version of the host-kit... 
I don't understand... Andreas will have to step in... I don't know 
why you're having that error.
Oldes
10-Nov-2010
[1723]
Actually it's:

../src/include/reb-lib.h:270:49: warning: __VA_ARGS__ can only appear 
in the expansion of a C99 variadic macro

wand.c:74:43: macro "RL_PRINT" passed 3 arguments, but takes just 
2
wand.c: In function `RX_Call':

wand.c:74: error: `RL_PRINT' undeclared (first use in this function)
Maxim
10-Nov-2010
[1724]
ok, well, I guess you'll have to use just two args for now  ;-)
Oldes
10-Nov-2010
[1725]
But I have a solution, this works:
RL->print("%d %s\n", n,item);
Maxim
10-Nov-2010
[1726x2]
true.


btw, keep reb-lib.h open when you code extension stuff, its the best 
source to get the proper argument information for all the available 
RLxxxxx functions.
if you look at the web version it mangles the argument types, by 
stripping away any pointer use... so it gets really screwed up.
Oldes
10-Nov-2010
[1728]
Extensions are quite additive as I'm getting closer to a real work 
:) I'm sure I will have other questions, but must force myself to 
stop now.
Maxim
10-Nov-2010
[1729]
yes, once you understand that just about everything obeys the same 
basic pattern, it gets quite easy and addictive, since you get results 
pretty quickly  :-)
Oldes
10-Nov-2010
[1730x2]
true, once I will understand the patterns, I will make a script, 
which will create the C code for me.
oh s**t, I cannot stop.. I'm already able to convert images..
Maxim
10-Nov-2010
[1732]
hahaha
Pekr
10-Nov-2010
[1733]
Oldes - LOL :-)
Gregg
11-Nov-2010
[1734]
:-)
Oldes
11-Nov-2010
[1735x3]
When I have integer (from REBOL side) and want to use it as a pointer, 
what I must do?
And, what would you do: (1.) Save wand pointers on REBOL side, or 
(2.) only on C side and provide just IDs (index) to this pointers 
(which is probably safer but would require dynamic array on the C 
side)?
I think I've found answer on my question, but please correct me, 
if there is better way:
            i32 magick_wand_ptr;
            MagickWand *magick_wand;
            
            magick_wand_ptr = RXA_INT32(frm, 1);
            magick_wand = magick_wand_ptr;
Cyphre
11-Nov-2010
[1738]
I think you could just do this:

magick_wand = (MagickWand*)RXA_INT32(frm, 1);
Oldes
11-Nov-2010
[1739x4]
Right, that works as well:)
I§m having problems to get string from REBOL input if it's unicode:/
So with Cyphre's help I have this function:
char* rebser_to_utf8(REBSER* series) {
    char *uf8str;
    REBCHR* str;
    REBINT result = RL_GET_STRING(series, 0 , (void**)&str);
        
    if (result > 0){
        //unicode string
        int iLen = wcslen(str);
        int oLen = iLen *  sizeof(REBCHR);
        uf8str = malloc(oLen);

        int result = WideCharToMultiByte(CP_UTF8, 0, str, iLen, uf8str, oLen, 
        0, 0);
        if (result == 0) {
            int err = GetLastError();
            RL->print("err: %d\n", err);
        }
    } else if (result < 0) {
        //bytes string (ascii or latin-1)
        uf8str = malloc(strlen((char *)str));
        strcpy(uf8str, (char *)str);
    }
    return uf8str;
}

and I can than use:
..
            char *filename = rebser_to_utf8(RXA_SERIES(frm, 1));
            status=MagickReadImage(current_wand, filename);
            free(filename);
            if (status == MagickFalse) {
                ThrowWandException(current_wand);
            }
            return RXR_TRUE;
Is it correct? It's working but maybe there is something I don't 
see as a C newbie.
BrianH
11-Nov-2010
[1743]
Why are you using an integer on the REBOL side to store a pointer? 
That is what handle! is for.
Oldes
11-Nov-2010
[1744]
because I don't know handle. I'm learning and there are no examples 
yet
BrianH
11-Nov-2010
[1745]
REBOL doesn't have pointers, it has references, and it doesn't have 
addresses. So the only way you could legitimately get a pointer is 
to return it from a command. But you don't want to have any way to 
construct an illegitimate pointer in REBOL and pass it to a command 
because that would be a much worse security and stability problem 
than just having commands at all, and treating pointers as integers 
lets you do that. So there is the handle! type to store pointers. 
A handle! is an immediate value that is the size of a pointer, but 
that you can't convert directly to or from any other value, or even 
mold it to see its contents. When you return a pointer from a command 
you set the value to the handle! type. Then that handle! will be 
usable when passed back to other commands in the same extension, 
and maybe even when passed to other extensions, depending on address 
space issues. Handles are also used to store function pointers in 
R3, and other opaque system values like library addresses.
Oldes
11-Nov-2010
[1746]
changed to use handle, but the string conversion function is probably 
bad.
BrianH
11-Nov-2010
[1747]
What you absolutely don't want to have available in REBOL code is 
address arithmetic. That should be reserved for native code.
Oldes
11-Nov-2010
[1748]
Right, now it's clear that I cannot mess with the wand pointers, 
so it's safe to store the wands on the REBOL side. Thanks for that 
info.
BrianH
11-Nov-2010
[1749]
And on that subject, what is the current state of string conversion 
in extensions? When last I checked it was really poor, all sorts 
of missing macros.
PeterWood
11-Nov-2010
[1750]
Oldes: HAve you tested the function with a string including a unicode 
code point which translates to a three-byte utf-8 character? The 
size of utf8str appears to beonly  twice the number of codepoints 
in the REBOL stirng.


A good example of a three-byte utf-8 character is the Euro sign - 
Unicode 20AC UTF-8 E2 82 AC
Oldes
11-Nov-2010
[1751]
Right, so what is the best way to allocate the buffer. Also the function 
as it is is working strange if I use it multiple times. So I guess 
the allocation is bad.
PeterWood
11-Nov-2010
[1752x2]
The maximum length of a utf-8 translation of a UCS-2 string would 
be 1.5 times the length of the string. So if wcslen returns the number 
of codepoints in a string, the length of the utf-8 should be the 
length of the str multiplied by 3 integer divided by 2 plus 1.
If wcslen returns the length in bytes then multiply it by 3.
Oldes
11-Nov-2010
[1754x2]
the problem with my function is, that the utf8 string is not null-terminated, 
how to do it properly?
int oLen = WideCharToMultiByte( CP_UTF8, 0, str, -1, NULL, 0,  NULL, 
NULL);
should be used to get the correct length.
PeterWood
11-Nov-2010
[1756]
I'd be surprised if WideChatToMultiByte didn't null-terminate the 
translated field. Have you checked that it isn't now that you are 
allocating the correct length?. .
Oldes
11-Nov-2010
[1757x2]
This seems to be working:
char* REBSER_to_UTF8(REBSER* series) {
    char *uf8str;
    REBCHR* str;
    REBINT result = RL_GET_STRING(series, 0 , (void**)&str);
        
    if (result > 0){
        //unicode string
        int iLen = wcslen(str);
        //int oLen = iLen *  sizeof(REBCHR);

        int oLen = WideCharToMultiByte( CP_UTF8, 0, str, -1, NULL, 0,  NULL, 
        NULL);
        uf8str = malloc(oLen);

        int result = WideCharToMultiByte(CP_UTF8, 0, str, iLen, uf8str, oLen, 
        0, 0);
        if (result == 0) {
            int err = GetLastError();
            RL->print("err: %d\n", err);
        }
        uf8str[oLen] = 0;
    } else if (result < 0) {
        //bytes string (ascii or latin-1)
        uf8str = strdup((char *)str);
    }
    return uf8str;
}
OK.. next step... how to create REBOL errors? :)
PeterWood
11-Nov-2010
[1759x2]
If it doesn't you would need to add one using pointer arithmetic. 
I think the code would be something like this::

char *last;
last = utf8str + result + 1;
*last = 0;
One question about your function. When do you free the memory you 
allocated to utf8str?
Oldes
11-Nov-2010
[1761x2]
when I don't need it.. so for example:
            char *filename = rebser_to_utf8(RXA_SERIES(frm, 1));
            status=MagickReadImage(current_wand, filename);
            free(filename);
Correct me, if it's bad behaviour or if there is better way. I'm 
really C newbie.
ChristianE
11-Nov-2010
[1763]
Is generating erros from C code possible with A110 (it wasn't before)? 
 I have yet to check that. It would be cool, I could simplify my 
ODBC extension using that.
Oldes
11-Nov-2010
[1764]
there is RXR_ERROR but hard to say how to use it. I was hoping you 
will know it:)
Maxim
11-Nov-2010
[1765x3]
Oldes, thanks for that UTF-8 function converter  :-)
we can return RXR_ERROR, but I don't think it really generates errors... 
though I haven't even tested it at length.
what you can do, is wrap your command within a function in the extension's 
module and expose that function.  this allows you to control the 
return type of the command better and handle it within the comfort 
of rebol.
Oldes
12-Nov-2010
[1768]
Again with Cyphre's help, here is a function which converts MultiByte 
(utf-8) string from C side to REBSER used to return the string to 
REBOL side:

REBSER* MultiByteToRebser(char* mbStr) {

    int len = MultiByteToWideChar( CP_UTF8, 0, mbStr, -1, NULL, 0);
    //the len is length of the string + null terminator
    wchar_t *wcStr = malloc(len * sizeof(wchar_t));

    int result = MultiByteToWideChar(CP_UTF8, 0, mbStr, strlen(mbStr), 
    wcStr, len);
    if (result == 0) {
        int err = GetLastError();
        RL->print("ERROR: MultiByteToWideChar -> %d\n", err);
        exit(-1); //how to throw ERROR on REBOL side?
    }
    REBSER *ser = RL_MAKE_STRING(len-1,TRUE);
    REBUNI *dst;

    //hack! - will set the tail to len
    REBINT *s = (REBINT*)ser;
    s[1] = len-1;

    RL_GET_STRING(ser,0,(void**)&dst);
    wcscpy(dst, wcStr);

    free(wcStr);
    wcStr = NULL;
    return ser;
}

I'm not sure how safe it is, but it seems to be working.
To return the string value I use:

RXA_TYPE(frm, 1) = RXT_STRING;
RXA_SERIES(frm, 1) = (REBSER *)MultiByteToRebser(utf8str);
return RXR_VALUE;