Page 1 of 1
Return an Object into a Cell from C++ Addin
Posted: Mon Dec 24, 2012 4:26 pm
by saleem145
Hello,
Lets says I have a class Rectangle whose constructor takes in length and width.
In OpenOffice Cell A1 I want to be able to type in
=Rectangle(10,20)
This should invoke the constructor and return a pointer to an object of type Rectangle.
In Cell A2 I want to able to type in
Area(A1)
This should call the area method of rectangle....
Is this possible??
Saleem
Re: Return an Object into a Cell from C++ Addin??
Posted: Mon Dec 24, 2012 8:41 pm
by Charlie Young
saleem145 wrote:Is this possible??
Saleem
I doubt it. What address space is A1 supposed to be pointing into? If the pointer is into some memory allocated by the function in A1, that memory will be freed when the function exits. It would seem you could return some long integer which pointed to one of your rectangles while the function was executing, but what might be at that address later is anybody's guess, I would think.
Re: Return an Object into a Cell from C++ Addin??
Posted: Tue Dec 25, 2012 10:10 pm
by Charlie Young
Charlie Young wrote:saleem145 wrote:Is this possible??
Saleem
I doubt it. What address space is A1 supposed to be pointing into? If the pointer is into some memory allocated by the function in A1, that memory will be freed when the function exits. It would seem you could return some long integer which pointed to one of your rectangles while the function was executing, but what might be at that address later is anybody's guess, I would think.
Life is full of surprises. Just for kicks, I tried this. I used function names RectPtr and RectArea since Rectangle and Area seemed too generic, but here are the details:
In the idl (with a bunch of other stuff in this case, since I just added these functions to an old project):
Code: Select all
long RectPtr([in] double x,[in] double y);
double RectArea([in] long RectPtr);
A rectangle class (mostly from cplusplus.com)
Code: Select all
class CRectangle {
double width, height;
public:
CRectangle (double,double);
double area ();
};
CRectangle::CRectangle (double a, double b) {
width = a;
height = b;
}
double CRectangle::area()
{
return (width*height);
}
And the add-in functions
Code: Select all
long SAL_CALL OldProjectImpl::RectPtr( double x, double y)
throw (RuntimeException)
{
CRectangle *rect;
rect = new CRectangle(x,y);
return (long) rect;
}
double SAL_CALL OldProjectImpl::RectArea( long RectPtr)
throw (RuntimeException)
{
CRectangle *rect;
rect = (CRectangle *) RectPtr;
return rect->area();
}
And it works!
If I put in A1
I get a long, like 247094384
and so in A2 I put
I get the answer 200, and if I I edit the parameters in A1, the changes are reflected in A2.
I'll be amazed if this generalizes very well, and I wouldn't be surprised If it fails ignominiously at some point, but it does look like something worth playing around with, at least.
Re: Return an Object into a Cell from C++ Addin
Posted: Wed Dec 26, 2012 9:08 pm
by saleem145
Two comments --
1. Is it possible to return the pointer tugged away somewhere in the Any Data Structure. I would the cell to display "*" in the cell indicating that is a pointer. But we can hide the pointer value in the datastructure and access it when we want.
2. It would be cool if there were just a way to export the functions into a pluggin without having to write the wrappers, xcus etc...
Saleem
Re: Return an Object into a Cell from C++ Addin
Posted: Wed Dec 26, 2012 9:33 pm
by saleem145
One more question -- how would this work on a 64 bit machine -- I set the return type to hyper in the idl file and used sal_Int64 in the .cc. But the cell displays a #VALUE error. I think returning any 64 bit integer forces it to display #VALUE....
Saleem
Re: Return an Object into a Cell from C++ Addin
Posted: Wed Dec 26, 2012 10:12 pm
by Charlie Young
saleem145 wrote:One more question -- how would this work on a 64 bit machine -- I set the return type to hyper in the idl file and used sal_Int64 in the .cc. But the cell displays a #VALUE error. I think returning any 64 bit integer forces it to display #VALUE....
Saleem
HYPER is not an allowed return type for an add-in function. The numeric value in the cell is in fact a double, your precision there is limited to 15 places, and I think you need 20 or so for 64 bits. A wild idea is to store the number as a string (maybe hex?), and have the function convert it to a number before using it to access memory.
The pointer to the data structure is the cell value, so how are you going to access it if it's in the data structure?
Re: Return an Object into a Cell from C++ Addin
Posted: Wed Dec 26, 2012 11:09 pm
by saleem145
How do we ensure the destructor is called if new objects are created. Instead of feeding in a fixed width and depth, try feeding in rand() so every calculate creates a new object....
Saleem
Re: Return an Object into a Cell from C++ Addin
Posted: Thu Dec 27, 2012 2:24 am
by Charlie Young
saleem145 wrote:How do we ensure the destructor is called if new objects are created. Instead of feeding in a fixed width and depth, try feeding in rand() so every calculate creates a new object....
Saleem
I tried that, I made about 10,000 rectangles with random dimensions, and never had a problem. Of course, our example so far is not much more than a roundabout way of multiplying two numbers, but you likely have something more ambitious in mind.
I've been playing with the 64 bit problem, using my "wild idea" incorporating hex strings. We just need some handy-dandy utility functions
Code: Select all
OUString Dec2OUHex(sal_uInt64 dec)
{
static sal_Unicode hexdigits[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
OUString hexStr = OUString();
sal_uInt64 Quotient;
int Remainder;
if(dec > 0) {
Quotient = dec;
while(Quotient > 0)
{
Remainder = Quotient % 16;
hexStr += OUString(hexdigits[Remainder]);
Quotient /= 16;
}
} else {
hexStr = OUString(hexdigits[0]);
}
return oureverse(hexStr);
}
OUString oureverse(OUString s)
{
OUString r;
r = OUString();
long i;
for(i = s.getLength() - 1;i >= 0; i--)
{
r += OUString(s[i]);
}
return r;
}
sal_uInt64 Hex2Dec64(OUString hexStr)
{
OUString r = oureverse(hexStr);
sal_uInt64 p,val = 0;
long i,j;
int d;
for(i = 0;i < r.getLength();i++)
{
if(r[i] >= '0' && r[i] <= '9')
d = r[i] - '0';
else
d = r[i] - 'A' + 10;
p = 1;
for(j = 0;j < i;j++)
p *= 16;
val += d * p;
}
return val;
}
Then RectPtr and RectArea become
Code: Select all
OUString SAL_CALL ClassAddinsImpl::RectPtr( double width, double height)
throw (RuntimeException)
{
CRectangle *rect;
rect = new CRectangle(width,height);
return Dec2OUHex((sal_uInt64) rect);
}
double SAL_CALL ClassAddinsImpl::RectArea( const OUString & RectPtr)
throw (RuntimeException)
{
CRectangle *rect;
sal_uInt64 ptr = Hex2Dec64(RectPtr);
rect = (CRectangle *) ptr;
return rect->area();
}
and it works (so far), but one thing to add is that since RectArea fetches the memory for whatever its argument points to, one should be careful with it.
Something like
Will very likely crash, as I've discovered.
Maybe something could (should) be done with try/catch.
A cell may be formatted to show * in place of a string, suppressing numbers altogether, with
Re: Return an Object into a Cell from C++ Addin
Posted: Thu Dec 27, 2012 3:12 pm
by saleem145
Thanks. I am working on the hex idea as well.
Saleem
BTW: it would be great if they extended the Any class to support user defined datatypes as well. And then the destructor would work as well.
Re: Return an Object into a Cell from C++ Addin
Posted: Thu Dec 27, 2012 6:30 pm
by Charlie Young
I always hate it when I post code and then realize I did something clunky.
Since we're using powers of 2, let's do bit shifting instead of multiplication and division.
Code: Select all
OUString Dec2OUHex(sal_uInt64 dec)
{
static sal_Unicode hexdigits[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
OUString hexStr = OUString();
if(dec > 0) {
while(dec > 0)
{
hexStr += OUString(hexdigits[dec % 16]);
dec >>= 4;
}
} else {
hexStr = OUString(hexdigits[0]);
}
return oureverse(hexStr);
}
OUString oureverse(OUString s)
{
OUString r;
r = OUString();
long i;
for(i = s.getLength() - 1;i >= 0; i--)
{
r += OUString(s[i]);
}
return r;
}
sal_uInt64 Hex2Dec64(OUString hexStr)
{
OUString r = oureverse(hexStr);
sal_uInt64 p,val = 0;
long i;
int d;
for(i = 0;i < r.getLength();i++)
{
if(r[i] >= '0' && r[i] <= '9')
d = r[i] - '0';
else
d = r[i] - 'A' + 10;
p = 1;
p <<= 4*i;
val += d * p;
}
return val;
}
int isOUxdigit(sal_Unicode c)
{
static sal_Unicode hexdigits[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
int d = 0, itsnot = 1;
while(itsnot && d < 16)
{
itsnot = c != hexdigits[d++];
}
return !itsnot;
}
I use isOUxdigit in RectArea, where I also use a catch(...). It seems with vc++ that is about the only way to deal with all the things that might go wrong here, and even at that it requires a special compiler switch (/EHa).
Code: Select all
double SAL_CALL ClassAddinsImpl::RectArea( const OUString & RectPtr)
throw (RuntimeException)
{
long i = 0;
while(i < RectPtr.getLength())
if(!isOUxdigit(RectPtr[i++]))
return -1;
try {
CRectangle *rect;
sal_uInt64 ptr = Hex2Dec64(RectPtr);
rect = (CRectangle *) ptr;
return rect->area();
} catch(...) {
return -1;
}
}
That assumes one doesn't want a rectangle with an area of -1.
It catches
and also
Code: Select all
=RECTAREA("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
RectPtr just checks for bad_alloc, which hasn't happened to me yet.
Code: Select all
OUString SAL_CALL ClassAddinsImpl::RectPtr( double width, double height)
throw (RuntimeException)
{
CRectangle *rect;
try {
rect = new CRectangle(width,height);
return Dec2OUHex((sal_uInt64) rect);
} catch (bad_alloc&) {
return OUString(RTL_CONSTASCII_USTRINGPARAM("Can't allocate rectangle"));
}
}
Edit:
Hmm, clunky code? Why isn't isOUxdigit just
Code: Select all
int isOUxdigit(sal_Unicode c)
{
return (c >= '0' && c <= '9') || (c >='A' && c <= 'F');
}
?????????????
Actually, it's because I quickly adapted it from another program, but that one could use some improvement as well.