0

This is an Objective-C macOS project created with Xcode version 13.2.1. Inside the project, I have a templated class named "plistModifier". The class is meant to set custom types of values for any plist (e.g. NSString, NSNumber etc.). The header file of the class is called "plistModifier.h" and it looks like this:

#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 */

The implementation of the class is as follows in a seperate "plistModifier.mm" file:

#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];
}

The issue is, when I create a plistModifier object and call it's methods, the compiler throws the error 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

This is how I call the methods of the object in SettingsVC.mm file:

#import "SettingsVC.h"
#include "plistModifier.h"

plistModifier<int> plistObject;

- (IBAction)debuggingToggle:(id)sender {
    
    plistObject.modifyPref("DebugEnabled", _debugToggle.state);
}

I have tried using the same templated class in an empty C++ project, and it didn't give me any errors. What do you think the problem is?

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
rA9
  • 3
  • 1
  • Are you sure that plistModifier.mm is added to the app target? Check "target membership" in file inspector (right panel). It must match with SettingsVC.mm. – battlmonstr Feb 04 '22 at 09:59
  • Yes, it is added. I ended up converting the templated class to a normal C++ class and treating all of the values inside the plist file as NSString. – rA9 Feb 04 '22 at 12:53

1 Answers1

0

It's not possible to split template declaration (.h) and definition (.mm/.cpp). You have to keep the template methods implementation in the .h file.

Refer to this explanation

https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl

But since your implementation is not using the generic type type properties, you could split it by implementing this in terms of id type in .mm, and then adding a templated wrapper in .h file:

// .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);
    }
    ...
}

This works as long as type can be implicitly casted to id, which is true for NSString, NSNumber, NSDate, NSArray and NSDictionary.

If you need to support primitive types like int, you need to specialize the template with an adjusted implementation.

battlmonstr
  • 5,841
  • 1
  • 23
  • 33