Tee*_*ppa 3 c++ iphone macos objective-c++
我昨天遇到了以下奇怪的行为.这对我来说似乎是一个编译器错误,还是有一些我错过的东西?我使用Objective-C到C++适配器类将Facebook Connect for iPhone的Objective-C类包装起来,这样就可以更方便地使用我们自己的OpenGL/C++代码.
以下代码显示了该问题.在下面的第一个变体中,编译器编译但是弄乱了vtable,因此调用了错误的方法.在第二个变体中,我们得到一个编译器错误,表明gcc很混乱.
评论试图更详细地解释这种情况.
#include <iostream>
#import <Foundation/Foundation.h>
// An abstract C++ interface
class Foo_cpp {
public:
virtual void foo() = 0;
};
// Another abstract C++ interface
class Bar_cpp {
public:
virtual void bar() = 0;
};
// An Objective-C to C++ adaptor.
// It takes a C++ interface Foo. When it's do_foo method is called it
// delegates call to Foo::foo.
@interface Foo_objc : NSObject {
Foo_cpp* foo_cpp_;
}
@end
@implementation Foo_objc
- (id)init:(Foo_cpp*)foo {
self = [super init];
if (self) {
foo_cpp_ = foo;
}
return self;
}
- (void) do_foo {
std::cout << "do_foo: ";
foo_cpp_->foo();
}
@end
// Another Objective-C to C++ adaptor.
@interface Bar_objc : NSObject{
Bar_cpp* bar_cpp_;
}
@end
@implementation Bar_objc
- (id)init:(Bar_cpp*)bar {
self = [super init];
if (self) {
bar_cpp_ = bar;
}
return self;
}
- (void) do_bar {
std::cout << "do_bar: ";
bar_cpp_->bar();
}
@end
// Main class implements both abstract C++ interfaces (which will
// confuse the compiler as we shall see).
// It constructs two Objective-C to C++ adaptors as a members and
// tries to pass itself as a C++ delegate for these adaptors.
class Main : public Foo_cpp, public Bar_cpp {
public:
Foo_objc* foo_;
Bar_objc* bar_;
Main() {
// We try to construct two objective-c to c++ adaptors Foo_objc and
// Bar_objc.
//
// We expect output of
// [foo_ do_foo];
// [bar_ do_bar];
// to be
// do_foo: foo
// do_bar: bar
#if 0
// This variant compiles but the compiler messes up
// the vtables. When do_bar() is called, we expect
// bar() to be called via Bar_objc, but instead
// foo() is called from both adaptors.
// Output is
// do_foo: foo
// do_bar: foo !!!! Calls wrong method !!!!
foo_ = [[Foo_objc alloc] init:this];
bar_ = [[Bar_objc alloc] init:this];
[foo_ do_foo];
[bar_ do_bar];
#else
// Now, this variant tries to help the compiler by passing
// |this| via a variable of the correct interface type.
// It actually reveals the confusion that the compiler
// is having. Seems like a bug in the compiler.
Foo_cpp* iface = this;
foo_ = [[Foo_objc alloc] init:iface];
Bar_cpp* iface2 = this;
// Error we get is on the next code line.
// $ g++ -x objective-c++ -lobjc mheritance_test.mm
// mheritance_test.mm: In constructor ‘Main::Main()’:
// mheritance_test.mm:107: error: cannot convert ‘Bar_cpp*’ to ‘Foo_cpp*’ in argument passing
bar_ = [[Bar_objc alloc] init:iface2];
[foo_ do_foo];
[bar_ do_bar];
#endif
}
~Main() {
delete foo_;
delete bar_;
}
virtual void foo() {
std::cout << "foo" << std::endl;
}
virtual void bar() {
std::cout << "bar" << std::endl;
}
};
int main() {
Main m;
}
Run Code Online (Sandbox Code Playgroud)
iPhone SDK和Mac自带的g ++以及4.0.1和4.2版本会出现问题.有没有我错误理解的东西或者这是g ++中的错误?
更新 我的例子中包含一个偶然的错误指出Tyler和Martin York,但这不是问题.以下是更新的示例.
#include <iostream>
#import <Foundation/Foundation.h>
// An abstract C++ interface
class Foo_cpp {
public:
virtual void foo() = 0;
};
// Another abstract C++ interface
class Bar_cpp {
public:
virtual void bar() = 0;
};
// An Objective-C to C++ adaptor.
// It takes a C++ interface Foo. When it's do_foo method is called it
// delegates call to Foo::foo.
@interface Foo_objc : NSObject {
Foo_cpp* foo_cpp_;
}
@end
@implementation Foo_objc
- (id)init:(Foo_cpp*)foo {
self = [super init];
if (self) {
foo_cpp_ = foo;
}
return self;
}
- (void) do_foo {
std::cout << "do_foo: ";
foo_cpp_->foo();
}
@end
// Another Objective-C to C++ adaptor.
@interface Bar_objc : NSObject{
Bar_cpp* bar_cpp_;
}
@end
@implementation Bar_objc
- (id)init:(Bar_cpp*)bar {
self = [super init];
if (self) {
bar_cpp_ = bar;
}
return self;
}
- (void) do_bar {
std::cout << "do_bar: ";
bar_cpp_->bar();
}
@end
class Main : public Foo_cpp, public Bar_cpp {
void foo() {
std::cout << "foo" << std::endl;
}
void bar() {
std::cout << "bar" << std::endl;
}
};
int main() {
Main* m = new Main;
#if 0
// Compiles but produces
// do_foo: foo
// do_bar: foo !!! incorrect method called !!!
Foo_objc* fo = [[Foo_objc alloc] init:m];
Bar_objc* bo = [[Bar_objc alloc] init:m];
#else
// Doesn't compile
Foo_objc* fo = [[Foo_objc alloc] init:(Foo_cpp*)m];
Bar_objc* bo = [[Bar_objc alloc] init:(Bar_cpp*)m];
// A line above produces following error
// mheritance_test2.mm: In function ‘int main()’:
// mheritance_test2.mm:82: error: cannot convert ‘Bar_cpp*’ to ‘Foo_cpp*’ in argument passing
#endif
[fo do_foo];
[bo do_bar];
}
Run Code Online (Sandbox Code Playgroud)
更新2 如果将init:Fooobjc和Barobjc的方法重命名为initfoo:和initbar:那么它可以正常工作,但我仍无法解释代码的问题.与Objective-C如何创建方法签名有关吗?
我正在编辑我的回答提示,因为我完成了任务;-)
我不是一个Objective-C程序员,但是出于好奇心,我不禁想知道发生了什么,并且稍微使用了代码.我发现问题在评论出除了Foo*和Bar*零件之外的所有内容并将以下行添加到main():
Bar_objc *bo = [[Bar_objc alloc] init:(Bar_cpp*)0];
Run Code Online (Sandbox Code Playgroud)
在玩了一下之后,我发现它必须与alloc消息的定义结果有关.通过将上面的分配分成两部分来解决这个问题:
Bar_objc *bo = [Bar_objc alloc]; [bo init:(Bar_cpp*)0];
Run Code Online (Sandbox Code Playgroud)
这很好用.铸造alloc结果也是如此(参见下面的代码).或者,这可以修复(我相信)具有不同的初始化器名称.也许还会重新实现alloc.不知道.
具有多重继承的完整代码(它还有一些其他的小改动 - 我为了简洁而将类/公共对更改为结构,删除了在构造函数中调用虚拟,将delete调用更改为dealloc消息,可能还有其他内容):
#include <iostream>
#import <Foundation/Foundation.h>
struct Foo_cpp { virtual void foo() = 0; };
struct Bar_cpp { virtual void bar() = 0; };
@interface Foo_objc : NSObject {
Foo_cpp* foo_cpp_;
}
- (id)init:(Foo_cpp*)foo;
- (void)do_foo;
@end
@implementation Foo_objc : NSObject {
Foo_cpp* foo_cpp_;
}
- (id)init:(Foo_cpp*)foo {
if( self = [super init] ) foo_cpp_ = foo;
return self;
}
- (void) do_foo { std::cout << "do_foo: "; foo_cpp_->foo(); }
@end
@interface Bar_objc : NSObject {
Bar_cpp* bar_cpp_;
}
- (id)init:(Bar_cpp*)bar;
- (void)do_bar;
@end
@implementation Bar_objc : NSObject {
Bar_cpp* bar_cpp_;
}
- (id)init:(Bar_cpp*)bar {
if( self = [super init] ) bar_cpp_ = bar;
return self;
}
- (void) do_bar { std::cout << "do_bar: "; bar_cpp_->bar(); }
@end
struct Main : public Foo_cpp, public Bar_cpp {
Foo_objc* foo_;
Bar_objc* bar_;
Main() {
foo_ = [(Foo_objc*)[Foo_objc alloc] init:this];
bar_ = [(Bar_objc*)[Bar_objc alloc] init:this];
}
~Main() { [foo_ dealloc]; [bar_ dealloc]; }
virtual void foo() { std::cout << "foo" << std::endl; }
virtual void bar() { std::cout << "bar" << std::endl; }
};
int main() {
Main m;
[m.foo_ do_foo];
[m.bar_ do_bar];
}
Run Code Online (Sandbox Code Playgroud)
结果:
do_foo: foo
do_bar: bar
Run Code Online (Sandbox Code Playgroud)
底线:我认为由于键入有点弱,并且无论类型如何都可以向对象发送消息,最好不要使用相同名称但不同参数的消息.
| 归档时间: |
|
| 查看次数: |
2324 次 |
| 最近记录: |