Discussion:
How to refer to cell value in xloper12?
(too old to reply)
XLjedi
2010-03-02 18:13:01 UTC
Permalink
Assuming "amounts" is defined as an input reference to a range of cells:
xloper12 *amounts

How do you reference the individual values contained in the amounts range?

I tried using:
for(DWORD i = 0; i < result_size; i++)
{
n = amounts[i].val.num;
result.SetArrayElt(i, n);
}

Where "n" is a double and "result" is Steve Dalton's cpp_xloper class, and
I'm getting strange (almost zero) values returned from my array function.
lab27
2010-03-02 19:15:33 UTC
Permalink
Inline
Post by XLjedi
xloper12 *amounts
How do you reference the individual values contained in the amounts range?
        for(DWORD i = 0; i < result_size; i++)
        {
                n = amounts[i].val.num;
                result.SetArrayElt(i, n);
        }
Where "n" is a double and "result" is Steve Dalton's cpp_xloper class, and
I'm getting strange (almost zero) values returned from my array function.
I'm assuming from the above that your function is registered with a
single input called "amounts" of type Q/U - given that:

A few things.

You're treating amounts like it's an array of XLOPER12s - it's not,
it's a pointer to a single XLOPER12, which will be of type xltypeRef/
xltypeSref/xltypeMulti [*] (which are more or less reference anywhere,
reference on 'current' sheet, anonymous range/array) - you need to
address the inner xltypeMulti if it's a multi, or coerce it to a multi
if it's not.

Look at the code I gave for your previous question, which handles just
this : http://groups.google.com/group/microsoft.public.excel.sdk/browse_thread/thread/265187675a9478df

---

what's to guarantee result_size is any relationship to the size of the
input data? Even if amounts was an array of XLOPER12s, you need to
check the size - for this, if you want to actually get at the data,
coerce to an xltypeMulti (again, see other thread), and look at the
size.

---

When dealing with a discriminated union (also called tagged union),
which is what an XLOPER(12) is, you MUST check the discriminator to
make sure that you're addressing it correctly. It's only valid to
look at the .val.num if the XLOPER's xltype is of type xltypeNum. In
the above case (given my original assumption holds true, it'll be
xltypeRef/xltypeSref/xltypeMulti ). [*]

Rgds,

Lee.

[*] unless you've used type Q and passed a 1x1 range, in which case
it'll be coerced to a value, and you'll get xltypeNum/string/whatever.
XLjedi
2010-03-02 21:24:01 UTC
Permalink
thanks again,

I have to admit I feel like I'm asking the most basic of questions and
continue to be baffled by the organization and structure of c++ code.

I've been put off a bit (not just by your example, but many) by comments at
the top of code that mark items as "not threadsafe". I was hoping by using
Dalton's cpp_xloper wrappers I could keep my formulas threadsafe. But again,
I know so little at this point, perhaps that isn't something I should be
worried about.

It is very difficult for someone just starting out. I'm looking at about
3-5 different books along with your examples and other web references and I
can see bits of answers here and there but of course nothing seems to be
compatible. Everything out there presumes a pretty solid foundation in c++,
very little is written or provided that is geared toward an excel user who
has outgrown the VBA mold for creating custom functions. I'm coming around,
but it's painful...

I was able to figure out how to take a range ref (*amounts) as an xloper12
and then create a range ref as an xloper12 (result) that matched the size of
the input. It may be crude, or totally the wrong way to go about it, but
I'll go ahead and post it for scrutiny:

arg types registered as: UU

xloper12 * __stdcall Allocate_xl12(xloper12 *amounts)
{
#pragma EXPORT

int rows;
int columns;

int rFirst = amounts->val.sref.ref.rwFirst;
int cFirst = amounts->val.sref.ref.colFirst;
int rLast = amounts->val.sref.ref.rwLast;
int cLast = amounts->val.sref.ref.colLast;

rows = rLast - rFirst + 1;
columns = cLast - cFirst + 1;

cpp_xloper result((RW)rows, (COL)columns);

if(!result.IsType(xltypeMulti))
return NULL;

DWORD result_size;
result.GetArraySize(result_size);

int x = 1;
for(DWORD i = 0; i < result_size; i++)
{
result.SetArrayElt(i, x++);
}

return result.ExtractXloper12();
}

This seems to allow me to array-enter my Allocate formula and return x++
incrementing over a range that exactly matches the shape of the *amounts
reference. And I'm using Dalton's cpp_xloper class wrapper.

So I guess the first questions I should ask are:
1) Is this threadsafe? (I think it is)
2) Is this a totally goofy approach? ...or am I OK?
lab27
2010-03-02 23:44:23 UTC
Permalink
inline
Post by XLjedi
thanks again,
I have to admit I feel like I'm asking the most basic of questions and
continue to be baffled by the organization and structure of c++ code.  
I've been put off a bit (not just by your example, but many) by comments at
the top of code that mark items as "not threadsafe".  I was hoping by using
Dalton's cpp_xloper wrappers I could keep my formulas threadsafe.  But again,
I know so little at this point, perhaps that isn't something I should be
worried about.
This is more a caveat than something there deliberately to scare you!
All I'm saying with this is that, /if/ you register your function as
threadsafe (something you have to go out of your way to do in excel
2007, and can't do at all before that), you will have problems if
you're not aware of thread safety issues.

While it's a good goal to want to write threadsafe XLL functions - do
you /really/ need to do so? If you don't mark your functions as
threadsafe (I.e. postfix the registered prototype with a (!)
(bang) ), you don't need to worry about this. And you don't /need/ to
do this. It's extremely useful if you're working on a 8+ core box,
and you're doing some nasty crunching which can be optimised to work
brilliantly on a core by core basis, but it sounds to me like this is
something you want to be working towards, not starting out at.

For the sake of treating this as a learning experience, to be honest,
I'd just ignore the entire concept of using the multithreaded
capabilities of excel 2007, and just be aware that it's an 'advanced'
issue that you'll come back to.
Post by XLjedi
It is very difficult for someone just starting out.  I'm looking at about
3-5 different books along with your examples and other web references and I
can see bits of answers here and there but of course nothing seems to be
compatible.  Everything out there presumes a pretty solid foundation in c++,
very little is written or provided that is geared toward an excel user who
has outgrown the VBA mold for creating custom functions.  I'm coming around,
but it's painful...
I'm sure you've looked at some c++ books - I recommend also having a
read of http://www.parashift.com/c++-faq-lite/
Post by XLjedi
I was able to figure out how to take a range ref (*amounts) as an xloper12
and then create a range ref as an xloper12 (result) that matched the size of
the input.  It may be crude, or totally the wrong way to go about it, but
I can't speak for the cpp_xloper class, as I don't have Steves' book,
but http://msdn.microsoft.com/en-us/library/aa730920.aspx indicates
that extractXloper12 is threadsafe. Again - ask youself if you really
need to care at this point! :)
Post by XLjedi
arg types registered as: UU
xloper12 * __stdcall Allocate_xl12(xloper12 *amounts)
{
#pragma EXPORT
        int rows;
        int columns;
        int rFirst = amounts->val.sref.ref.rwFirst;
        int cFirst = amounts->val.sref.ref.colFirst;
        int rLast = amounts->val.sref.ref.rwLast;
        int cLast = amounts->val.sref.ref.colLast;
You /have/ to check that amounts->xltype is xltypeSref. If it isn't,
this just plain doesn't make sense. See my comment in the previous
post about 'discriminated unions'.
If you do this and give an off sheet reference, you will NOT get an
xltypeSref. If you call this with a single literal argument of 1 (so
=foo(1)), you will get an XLOPER of type xltypeNum. If you called it
with =foo({1,2;3,4}), you'd get an xlTypeMulti. etc, etc. You /have/
to check the type of the input.

Essentially the discriminated union says here, "if the amounts->xltype
ISN'T xltypeSref, then amounts->val.sref.* is /utterly meaningless/".

read this : http://www.drdobbs.com/cpp/184401905
Post by XLjedi
        rows = rLast - rFirst + 1;
        columns = cLast - cFirst + 1;
        cpp_xloper result((RW)rows, (COL)columns);
        if(!result.IsType(xltypeMulti))
                return NULL;
        DWORD result_size;
        result.GetArraySize(result_size);
        int x = 1;
        for(DWORD i = 0; i < result_size; i++)
        {
                result.SetArrayElt(i, x++);
        }
        return result.ExtractXloper12();
}
This seems to allow me to array-enter my Allocate formula and return x++
incrementing over a range that exactly matches the shape of the *amounts
reference.  And I'm using Dalton's cpp_xloper class wrapper.
1)  Is this threadsafe?  (I think it is)
Given the above msdn reference, it looks to be so.
Post by XLjedi
2)  Is this a totally goofy approach?  ...or am I OK?
You're not doing anything with the input data. ;) Seriously, this is
the most important thing to do - make sure you're comfortable with
what inputs are being given to your function. (or use the cpp_xloper
to wrap all interactions you have with XLOPER*, I'm sure there's
useful functionality to do so)..... There's nothing wrong with
attaching a debugger to excel and breakpointing at your functions'
entry point to actually examine the contents of the xloper.

Hope that helps.

Lee.
lab27
2010-03-03 08:33:15 UTC
Permalink
Post by lab27
While it's a good goal to want to write threadsafe XLL functions - do
you /really/ need to do so?  If you don't mark your functions as
threadsafe  (I.e. postfix the registered prototype with a (!)
(bang) ), you don't need to worry about this.  And you don't /need/ to
Err, dollar, not bang. Duh.
XLjedi
2010-03-03 18:05:01 UTC
Permalink
Lee,

Thanks for all of your very helpful and enlightening input.

The function that I'm working on has existed as a VBA user defined function
but crunches on very large ranges (matrices of 40x2000+ cells) and returns a
2000+ cell single row/column array. This causes our models to take 30
seconds or more between calculations. Which wouldn't be too bad if I didn't
have to run 100+ iterations on the model.

At this point my only option for improving the performance (1 to 3 second
range) is moving to a c++ XLL. It is possible that a single thread formula
will be adequate. I did not appreciate that "threadsafe" was something that
by default was not implemented, so that information alone allows me to breath
a sigh of relief.

My other challenge has been dealing with this c++ concept of freeing memory
after use and/or "memory leaks". Obviously, not something that a "finance
guy"--"hobby VB developer" would have been exposed to even after 10 years of
working with Excel/VB/VBA. So, I have also been using Steve's cpp_xloper
wrapper with his ExtractXLoper12 method to try to avoid some of this. It may
very well be that I just can't ignore it and need to start nosing around with
registering items to be cleaned up inside the the AutoFree code.

I will attempt to restudy your examples and see if I can make something work
for my situation. I do understand what you're saying about checking the
reference type first and then coercing to values. I'll start with that and
see how far I can get.

Thanks again!

Loading...