在 Objective-C++ (.mm) 文件中使用模板化 class 时的未定义符号
Undefined symbol when using a templated class in Objective-C++ (.mm) file
这是使用 Xcode 版本 13.2.1 创建的 Objective-C macOS 项目。在项目内部,我有一个名为“plistModifier”的模板化 class。 class 用于为任何 plist 设置自定义类型的值(例如 NSString
、NSNumber
等)。 class 的头文件名为“plistModifier.h”,它看起来像这样:
#ifndef plistModifier_h
#define plistModifier_h
#include <iostream>
#include <string>
#import <Foundation/Foundation.h>
#include <unistd.h>
template <class type>
class plistModifier {
public:
void modifyPref(std::string key, type value);
type getPref(std::string key);
};
#endif /* plistModifier_h */
class 的实现在单独的“plistModifier.mm”文件中如下:
#include "plistModifier.h"
template <class type>
void plistModifier<type>::modifyPref(std::string key, type val) {
NSString* preferencePlist = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/com.rA9.LeetDownPreferences.plist"];
NSDictionary* dict=[[NSDictionary alloc] initWithContentsOfFile:preferencePlist];
[dict setValue:val forKey:[NSString stringWithUTF8String:val]];
[dict writeToFile:preferencePlist atomically:YES];
}
template <class type>
type plistModifier<type>::getPref(std::string key) {
NSString *preferencePlist = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/com.rA9.LeetDownPreferences.plist"];
NSDictionary *dict=[[NSDictionary alloc] initWithContentsOfFile:preferencePlist];
return dict[key];
}
问题是,当我创建一个 plistModifier 对象并调用它的方法时,编译器抛出错误 Undefined symbols for architecture x86_64: "plistModifier<int>::modifyPref(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int)", referenced from: -[SettingsVC debuggingToggle:] in SettingsVC.o
这就是我在 SettingsVC.mm
文件中调用对象方法的方式:
#import "SettingsVC.h"
#include "plistModifier.h"
plistModifier<int> plistObject;
- (IBAction)debuggingToggle:(id)sender {
plistObject.modifyPref("DebugEnabled", _debugToggle.state);
}
我曾尝试在一个空的 C++ 项目中使用相同的模板化 class,它没有给我任何错误。您认为问题是什么?
无法拆分模板声明 (.h) 和定义 (.mm/.cpp)。
您必须在 .h 文件中保留模板方法实现。
Refer to this explanation
https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl
但是由于您的实现没有使用通用类型 type
属性,您可以通过在 .mm 中根据 id
type 实现它来拆分它,然后在 .h 中添加模板包装器文件:
// .mm
void plistModifierImpl::modifyPref(std::string key, id val) {
NSString *preferencePlist = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/com.rA9.LeetDownPreferences.plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:preferencePlist];
[dict setValue:val forKey:[NSString stringWithUTF8String:val]];
[dict writeToFile:preferencePlist atomically:YES];
}
// .h
class plistModifierImpl; // define this in .mm
template <class type>
class plistModifier {
// private loosely-typed implementation
std::unique_ptr<plistModifierImpl> p_impl;
public:
// public strongly-typed wrapper
void modifyPref(std::string key, type value) {
p_impl->modifyPref(key, value);
}
...
}
只要 type
可以隐式转换为 id
,这对 NSString、NSNumber、NSDate、NSArray 和 NSDictionary 都是正确的。
如果您需要支持像 int
这样的原始类型,您需要使用调整后的实现来专门化模板。
这是使用 Xcode 版本 13.2.1 创建的 Objective-C macOS 项目。在项目内部,我有一个名为“plistModifier”的模板化 class。 class 用于为任何 plist 设置自定义类型的值(例如 NSString
、NSNumber
等)。 class 的头文件名为“plistModifier.h”,它看起来像这样:
#ifndef plistModifier_h
#define plistModifier_h
#include <iostream>
#include <string>
#import <Foundation/Foundation.h>
#include <unistd.h>
template <class type>
class plistModifier {
public:
void modifyPref(std::string key, type value);
type getPref(std::string key);
};
#endif /* plistModifier_h */
class 的实现在单独的“plistModifier.mm”文件中如下:
#include "plistModifier.h"
template <class type>
void plistModifier<type>::modifyPref(std::string key, type val) {
NSString* preferencePlist = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/com.rA9.LeetDownPreferences.plist"];
NSDictionary* dict=[[NSDictionary alloc] initWithContentsOfFile:preferencePlist];
[dict setValue:val forKey:[NSString stringWithUTF8String:val]];
[dict writeToFile:preferencePlist atomically:YES];
}
template <class type>
type plistModifier<type>::getPref(std::string key) {
NSString *preferencePlist = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/com.rA9.LeetDownPreferences.plist"];
NSDictionary *dict=[[NSDictionary alloc] initWithContentsOfFile:preferencePlist];
return dict[key];
}
问题是,当我创建一个 plistModifier 对象并调用它的方法时,编译器抛出错误 Undefined symbols for architecture x86_64: "plistModifier<int>::modifyPref(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int)", referenced from: -[SettingsVC debuggingToggle:] in SettingsVC.o
这就是我在 SettingsVC.mm
文件中调用对象方法的方式:
#import "SettingsVC.h"
#include "plistModifier.h"
plistModifier<int> plistObject;
- (IBAction)debuggingToggle:(id)sender {
plistObject.modifyPref("DebugEnabled", _debugToggle.state);
}
我曾尝试在一个空的 C++ 项目中使用相同的模板化 class,它没有给我任何错误。您认为问题是什么?
无法拆分模板声明 (.h) 和定义 (.mm/.cpp)。 您必须在 .h 文件中保留模板方法实现。
Refer to this explanation
https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl
但是由于您的实现没有使用通用类型 type
属性,您可以通过在 .mm 中根据 id
type 实现它来拆分它,然后在 .h 中添加模板包装器文件:
// .mm
void plistModifierImpl::modifyPref(std::string key, id val) {
NSString *preferencePlist = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/com.rA9.LeetDownPreferences.plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:preferencePlist];
[dict setValue:val forKey:[NSString stringWithUTF8String:val]];
[dict writeToFile:preferencePlist atomically:YES];
}
// .h
class plistModifierImpl; // define this in .mm
template <class type>
class plistModifier {
// private loosely-typed implementation
std::unique_ptr<plistModifierImpl> p_impl;
public:
// public strongly-typed wrapper
void modifyPref(std::string key, type value) {
p_impl->modifyPref(key, value);
}
...
}
只要 type
可以隐式转换为 id
,这对 NSString、NSNumber、NSDate、NSArray 和 NSDictionary 都是正确的。
如果您需要支持像 int
这样的原始类型,您需要使用调整后的实现来专门化模板。