好的,这只是一个小小的警告。我目前正在与ESRI的可爱ArcSDK合作。现在要从他们的任何函数中获取值,你基本上必须传递变量,你想要赋值给。
E.g:
long output_width;
IRasterProps->get_Width(&output_width);
这是一件小事,但是当你必须从他们的各种功能中挑出大约30个不同的数据时,它真的开始变得烦人。
所以我想知道是否有可能通过STL或C ++的魔力以某种方式将其改为:
long output_width = IRasterProps->get_Width(<something magical>);
所有函数都返回void,否则它们中的一些可能会返回一个HRESULT,我可以放心地忽略它。有什么想法吗?
*** **** EDIT
继承了我得到的最终结果:)!!!!!
A magic(P p, R (__stdcall T::*f)(A *)) {
A a;
((*p).*f)(&a);
return a;
}
答案 0 :(得分:7)
我知道我已经回答了,但这是另一种方式。它更好,因为它更快(没有boost ::函数开销)并且避免了绑定器(因为人们似乎厌恶它们),但更糟糕的是它不那么通用(因为它只适用于单参数成员函数) )。
template <typename P, typename T, typename A>
A magic(P p, void (T::*f)(A &)) {
A a;
((*p).*f)(a);
return a;
}
您可以这样称呼:
long output_width = magic(raster_props_object, &IRasterProps::get_Width);
或者,如果你碰巧使用GCC,我们可以使用更多技巧:
#define MORE_MAGIC(p,f) ({ \
typedef __typeof(*(p)) big_ugly_identifier; \
magic((p),(&big_ugly_identifier::f)); \
})
让我们这样做:
long output_width = MORE_MAGIC(raster_props_object, get_Width);
(如果命名惯例让您想到PDP-10,则可获得奖励。)
编辑:更新为采用任何类似指针的类型,因此它现在可以使用shared_ptr,迭代器,并希望_com_ptr。
编辑:糟糕,它们是指针,而不是引用。这是一个处理它的版本(或重载),并允许 - 通过忽略 - 任意类型的返回值。
template <typename P, typename T, typename A, typename R>
A magic(P p, R (T::*f)(A *)) {
A a;
((*p).*f)(&a);
return a;
}
答案 1 :(得分:2)
这不是你指定的,因为你需要在方法周围包装get(),但它可以工作:
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;
}
答案 2 :(得分:1)
编辑:回想起来,我不确定这个实际上是否有效,因为我认为模板参数不会推断出来。买家当心。
当然!你需要的是你可以传递一个函数来调用它并返回输出值。
这是一种简单易用的方式:
template <typename T>
T magic(boost::function<void(T&)> f) {
T x;
f(x);
return x;
}
然后使用boost :: lambda:
进行调用long output_width = magic(raster_props_object->*&IRasterProps::get_Width);
或者像这样,使用boost :: bind:
long output_width = magic(bind(&IRasterProps::get_Width, raster_props_object, _1));
你可以摆脱boost :: function,但那更加丑陋。不过可能值得。
答案 3 :(得分:1)
你能从IRasterProps派生出来吗?因此,您可以构建自己的接口。
编辑:根据这个概念,您可以应用适配器设计模式(如果您希望将一个通用接口应用于几个相似的SDK类),甚至可以应用Facade。
答案 4 :(得分:1)
看起来像我的COM对象。 Visual C ++支持#import指令来导入类型库,并创建高级别的包装器。所以你最终得到了
width = ptr->GetWidth();
或 - 甚至更好 -
width = ptr->Width;
如果函数失败,返回的HRESULT将转换为_com_error
异常。
我在许多操作系统和第三方COM对象上成功使用它,使它们更容易使用。
请注意,您通过选项控制包装器生成,我做的第一件事通常是添加rename_namespace
或no_namespace
,因为否则symbold最终会在名称空间中根据类型库名称进行操作,通常是丑陋的。
另外,除非您使用named_guids
选项,否则可能需要将CLSID_xxx
和IID_xxx
常量更改为__uuidof(xxx)
。
答案 5 :(得分:0)
不要认为这是可能的。在任何情况下,将void分配给long应该是一个错误。
请记住,传递引用可能比返回大对象更高效。 (虽然不会对长期有所影响)
编译:
void foo(long &a) {
}
int main(void) {
long a=0;
a = foo(a);
return 0;
}
给出了这个错误:
g++ x.cc
x.cc: In function ‘int main()’:
x.cc:9: error: void value not ignored as it ought to be
答案 6 :(得分:0)
我不知道你能做些什么疯狂,就像你问的那样,如果有一些疯狂的hackery在一些特殊的平台上工作,我很确定在代码审查中我讨厌它
可能更有意义......
围绕您关心的API定义一些简单的内联函数包装
从IRasterProps(或其他)提供一个专门的类,它提供适当的访问器方法。
其中任何一个都会影响代码的维护时间,但会安全,干净地为您提供您正在寻找的调用语法。