tags:

views:

217

answers:

7

Okay, this is just a minor caveat. I am currently working with the lovely ArcSDK from ESRI. Now to get a value from any of their functions, you basically have to pass the variable, you want to assign the value to.

E.g.:

long output_width;
IRasterProps->get_Width(&output_width);

Its such a minor thing, but when you have to pick out around 30 different pieces of data from their miscellaneous functions, it really starts to get annoying.

So what i was wondering is it possible to somehow by the magic of STL or C++ change this into:

long output_width = IRasterProps->get_Width(<something magical>);

All of the functions return void, otherwise the off chance some of them might return a HRESULT, which i can safely ignore. Any ideas?

*EDIT**

Heres the final result i got which works :)!!!!!

A magic(P p, R (__stdcall T::*f)(A *)) {
    A a;
    ((*p).*f)(&a);
    return a;
}
A: 

Don't think this is possible. Assigning void to a long should be an error in any case.

Remember, it's probably more performant to pass-by-reference than to return a large object. (won't really make a difference with long's though)

Compiling this:

void foo(long &a) {
}

    int main(void) {

    long a=0;
    a = foo(a);
    return 0;
}

gives this error:

g++ x.cc 
x.cc: In function ‘int main()’:
x.cc:9: error: void value not ignored as it ought to be
Glen
+2  A: 

This is not quite what you specified because you need to wrap get() around the method, but it works:

template<class T, class S>
T get(S fun(T&)) {
  T result;
  fun(result);
  return result;
}

void foo(int& x) {
  x = 5;
}

bool bar(char& x) {
  x = 'c';
  return false;
}

int main() {
  int x = get(foo);
  char y = get(bar);

  return 0;
}
sepp2k
Interesting. Could this be adapted to work with an object / member function pointer? Don't see why not really. Though it does make the code harder to read / debug
Glen
UberJumper
Except it doesn't work for objects i.e. get(IRasterProps->get_Width) won't work and neither will get(IRasterProps::getWidth)
sbk
Take a look at me22's answer which extends this concept to objects sans boost
GRB
A: 

I'm not aware of something insane you could do, precisely like you're asking, and if there was some insane hackery that did work on some peculiar platform I'm pretty sure in a code-review I'd hate it.

It may may more sense to either...

  • define some trivial inline function wrappers around the APIs you care about

  • make a specialized class descend from IRasterProps (or whatever) that provides the appropriate accessor methods.

Either of those will impact maintenance time of the code but would safely and cleanly give you the call syntax you are looking for.

ted_j
Its very platform dependent, since ArcSDK C++ Api only compiles on 2003-2005. The thing is, the API is massive, and spans many different interfaces, its just rather frusturating when you have to do this crap: ISpatialReferencePtr ipSpatialRef; ipRasterProps->get_SpatialReference( BSTR alias; esriPrecisionImplHandle precision, ipSpatialRef->get_alias(
UberJumper
+1  A: 

EDIT: In retrospect, I'm not sure this one will actually work, since I don't think the template arguments will deduce. Buyer Beware.

Sure! What you need is something to which you can pass a function that will call it and return you the outputted value.

Here's the easy, if less efficient way:

template <typename T>
T magic(boost::function<void(T&)> f) {
    T x;
    f(x);
    return x;
}

Which you'd then call like this using boost::lambda:

long output_width = magic(raster_props_object->*&IRasterProps::get_Width);

Or like this, using boost::bind:

long output_width = magic(bind(&IRasterProps::get_Width, raster_props_object, _1));

You can get rid of boost::function, but that's uglier. Probably worth it, though.

me22
That's a lot of typing instead of doing long output_width; RasterProps->get_Width(
Glen
Would it be possible for you to demonstrate how that works without boost function? Since i know std::function is nowhere near as flexible. But thats amazing.
UberJumper
What I was thinking of is getting rid of boost::function and keeping the binders, but I can't see a nice way of getting the expected argument type out of a bind results -- and there might not be one, since it seems to just use argument forwarding on templated types -- and while the std ones do give various forms of argument_type member typedefs, IIRC they don't handle reference arguments properly. I'd be glad to be proven wrong, though :)
me22
+1  A: 

Can you derive from IRasterProps? Being that the case you can construct your own interface to it.

EDIT: Following on the concept you can probably also apply the Adapter design pattern (or even a Facade if you wish to apply a common interface to several like-minded classes of the SDK).

Krugar
+1 for the Adapter design pattern.
plinth
+7  A: 

I know I've already answered, but here's another way. It's better in that it's faster (no boost::function overhead) and avoids the binders (since people seem to have an aversion to them), but is worse in that it's much less general (since it only works for one-argument member functions).

template <typename P, typename T, typename A>
A magic(P p, void (T::*f)(A &)) {
    A a;
    ((*p).*f)(a);
    return a;
}

Which you'd call like this:

long output_width = magic(raster_props_object, &IRasterProps::get_Width);

Or, if you happen to be using GCC, we can use some more tricks:

#define MORE_MAGIC(p,f) ({ \
    typedef __typeof(*(p)) big_ugly_identifier; \
    magic((p),(&big_ugly_identifier::f)); \
})

Which will let us do this:

long output_width = MORE_MAGIC(raster_props_object, get_Width);

(Bonus points if the naming conventions made you think of a PDP-10.)

EDIT: Updated to take any pointer-like type, so it will now work with shared_ptr, iterators, and hopefully _com_ptr.

EDIT: Oops, they're pointers, not references. Here's a version (or overload) that deals with that, and allows -- by ignoring -- arbitrarily-typed return values.

template <typename P, typename T, typename A, typename R>
A magic(P p, R (T::*f)(A *)) {
    A a;
    ((*p).*f)(&a);
    return a;
}
me22
Why do you not need to explicitily state the template parameters?
UberJumper
+1, nice one...
Naveen
uberjumper: They're deduced from the type of the member function pointer.
me22
Error 1 error C2751: 'T::f' : the name of a function parameter cannot be qualified
UberJumper
sbk
Oops, thanks -- I forgot the * to make it a pointer.
me22
UberJumper
uberjumper: it looks like you'll have to add overloads for each of the different calling conventions.
me22
sbk: Ah, but you forget that this is C++, so we have macros to do extra-syntactic tricks. Stay tuned...
me22
me22 how would i go about doing it? I am really getting wayyyyyy over my head i think :(
UberJumper
Actually, uberjumper, that's a good point. The member function pointer is sufficient to deduce those types, but there's no need for the pointer-like object to match, so it can be a separate template argument, which will let it work with shared_ptr and iterators and similar.
me22
Try it without the overload first, but if you do need it, I think it'd just be this: template <typename P, typename T, typename A> A magic(P p, void (__thiscall T::*f)(A ((*p).*f)(a); return a; }
me22
I edited my question, with my response since it was not fitting :(
UberJumper
Oops, I completely missed the address-of operator in your initial question. Answer updated.
me22
That still didnt work, but i wrote an overloaded one, and decided at random to change __thiscall to __stdcall, which it works beautifully now. I'll edit the question and put what i changed.
UberJumper
Thank you so much btw.
UberJumper
+1  A: 

Looks like a COM object to me. Visual C++ supports an #import directive to import the type library, and create high-legel wrappers. So you either end up with

width = ptr->GetWidth();

or - even better -

width = ptr->Width;

If a function fails, the HRESULT returned will be transformed into an _com_error exception.

I've used that successfully on many OS and 3rd party COM objects, makes them much easier to use.

Note that you control the wrapper generation through options, the first thing I do is usually adding a rename_namespace or no_namespace, because otherwise the symbold end up in a namespace depending on the typelib name, which is usually ugly.

also, unless you use named_guids option, you might needto change CLSID_xxx and IID_xxx constants to __uuidof(xxx).

peterchen