"Use of undeclared identifier" in Objective-C++ when importing from another source file - objective-c++

I have declared a functions in a header file like this (pseudo code):
// funcs.h
#import <Foundation/Foundation.h>
NSString* func1();
void func2();
Then I have the implementations in
//funcs.mm
#import "funcs.h"
NSString* func1()
{
// do something and use C++ functions
}
void func2()
{
// do more
}
When I include the header in a third file
// AppDelegate.mm
#import "funcs.h"
....
string1 = func1();
func2();
....
Whenever I try to use these functions I get "Use of undeclared identifier" errors.
What am I doing wrong? Isn't function declaration in files in Objective-C++ the same as in normal C?

A .mm file is Objective-C++. Plain functions in a .mm file are not C functions, but C++ functions. Objective-C is the same as C, but you are not using Objective-C, you are using Objective-C++.
I would strongly recommend that you only use .mm files for interfacing with C++. For example, your AppDelegate should be a .m file and not .mm.
You need to write C functions like this:
extern "C" {
NSString* func1()
{
// do something and use C++ functions
}
}
Also, in the .h file you should declare them like this:
#if __cplusplus
extern "C" {
#endif
NSString* func1(void);
#if __cplusplus
}
#endif
The macro __cplusplus might be misspelled, google for it.
extern "C" tells the C++ or Objective-C++ compiler that a function should have C linkage.

Related

Custom TEST pre-processor macros in Swift 5

In Swift, I can do:
#if DEBUG
// code
#else
// code
#endif
but making custom macros as described in #ifdef replacement in the Swift language answers doesn't work for me.
How am I supposed to do this for Swift 5 / Xcode 12?
Edit:
My problem was that I assumed testing an application would trigger the macros in the application if I defined them in the AppTest target.
Putting -DYOURMACRO in Other Swift Flags under Swift Compiler - Custom Flags does indeed work.
The way to do what I wanted is either to put this flag in the application target whenever I want to run unit tests (really don't like this solution), or using launch arguments instead (not optimal but will do).
In C, Objective-C and Metal you have to use #ifdef DEBUG (Xcode 12.5):
#import <Foundation/Foundation.h>
#ifdef DEBUG
BOOL const DEBUG_BUILD = YES;
#else
BOOL const DEBUG_BUILD = NO;
#endif
But in Swift you have to use just the following syntax (Xcode 12.5):
import Foundation
#if DEBUG
let debugBuild: Bool = true
#else
let debugBuild: Bool = false
#endif
And why you said you couldn't use a custom pre-processor macros?
#if !RELEASE
import SceneKit
#endif
func loader() {
#if DEBUG
SceneKit.SCNSphere.init(radius: 0.1)
#endif
}

How do you use Lumberjack logging in Swift?

I'm in the process of converting an Objective-C file which uses Lumberjack Logging into Swift. It seems to be mostly working, except for the part where I declare ddloglevel.
The Objective-C way to do this:
#ifdef DEBUG
static const int ddLogLevel = LOG_LEVEL_INFO;
#else
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#endif
The Swift way:
#if DEBUG
let ddLogLevel = LOG_LEVEL_INFO;
#else
let ddLogLevel = LOG_LEVEL_VERBOSE;
#endif
Except I'm this compile time error: Use of unresolved identifier 'LOG_LEVEL_INFO'
Why is this happening? How can I fix it?
You can use workaround. Instead of setting logLevel globally (possible only in Objective-C), you could set logging level to all Loggers explicitly.
Example:
class LoggerFactory {
#if DEBUG
static let defaultLogLevel: DDLogLevel = DDLogLevel.All
#else
static let defaultLogLevel: DDLogLevel = DDLogLevel.Info
#endif
static func initLogging() {
DDLog.addLogger(DDTTYLogger.sharedInstance(), withLevel: defaultLogLevel)
DDLog.addLogger(DDASLLogger.sharedInstance(), withLevel: defaultLogLevel)
}
Looking at the library source, LOG_LEVEL_INFO and LOG_LEVEL_VERBOSE are #define macros, which Swift does not automatically import. Swift only sees const's.
However, I think that your approach as a whole might not make sense - it looks like you're trying to assign a value to the global Objective-C constant ddLogLevel. Swift's let is not going to do that for you - it's a completely different namespace. This is on top of not being able to use Objective-C macros in Swift.
Your best bet is to leave an Objective-C file in your project (called, say, LoggingConfig.m that just contains:
#import "DDLog.h"
#ifdef DEBUG
static const int ddLogLevel = LOG_LEVEL_INFO;
#else
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#endif
The library you're using relies heavily on Objective-C features, so best to just use Objective-C to configure it.
Edit: Looking at this library in more detail (I'd never heard of it before), using CocoaLumberjack at all in Swift is probably not going to work out, as its primary API is a preprocessor macro named DDLog. I don't think it's a good match.

Objective-C Constant variable not work, occurs "Property not found on object of type"

In Java, the following code is fine, no error.
class ConstantA{
public static String MY_TEST = "My Test";
}
import ConstantA;
Class TestClass{
public void test(){
System.out.println(ConstantA.MY_TEST); // it's work fine.
}
}
According to above java concept, it doesn't work on Objective-C,
in ConstantA.h file
extern NSString * const MY_TEST;
#interface ConstantA : NSObject
#end
in ConstantA.m file
NSString * const MY_TEST = #"My Test";
#implementation ConstantA
#end
in main.m file ( error occurs here)
#import "ConstantA.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
NSLog(#"%#",ConstantA.MY_TEST); // error: Property 'MY_TEST' not found on object of type 'ConstantA'
}
}
Can help me to solve this problem?
thanks so much
#import "ConstantA.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
NSLog(#"%#", MY_TEST); // error: Property 'MY_TEST' not found on object of type 'ConstantA'
}
}
Use directly the global variable, without class name:
MY_TEST and NOT ConstantA.MY_TEST.
Use NSLog(#"%#",MY_TEST);.
MY_TEST is not scoped by ConstantA in any way; it's simply a global variable. Objective-C doesn't support class-level "static variables" like you'd find in Java or C++. Classes can only have instance variables
You can sorta imitate a class variable with a global variable, but it's still just a global; it's not namespaced to the class in any way.
Put your Global string in the .h file
property (nonatomic, retain) NSString *gMyString;
In the .m file
synthetize gMyString;
It should work... I had the same problem last week !

Trouble using opaque pointers in Objective C++

The answer to this quesion explains that opaque pointers are a good way to include C++ member variables in an Objective C++ header. I'm getting compile errors when trying to follow the example. Here's the relevant code from my header, with the corresponding compiler errors shown as comments:
struct ADSR_opaque; // error: forward declaration of 'struct ADSR_opaque'
#interface LoopyPulser : NSObject{
float _pulseRate;
UInt32 tickInterval;
UInt32 step;
InMemoryAudioFile * audioFilePlayer;
ADSR_opaque* env; // error: expected specifier-qualifier-list before 'ADSR_opaque'
Pattern * pattern;
float loopLengthRatio;
float volume;
}
Is there something simple I'm doing wrong here?
I don't have any problem with the following minimal sample:
struct ADSR_opaque;
#interface LoopyPulser : NSObject {
struct ADSR_opaque* env;
}
#end
If you include the header in plain Objective-C files (not Objective-C++), you have to add struct.
Alternatively use typedefs:
struct ADSR_opaque_;
typedef struct ADSR_opaque_ ADSR_opaque;
#interface LoopyPulser : NSObject {
ADSR_opaque* env;
// ...

Static string variable in Objective C on iphone

How to create & access static string in iPhone (objective c)?
I declare static NSString *str = #"OldValue" in class A.
If i assign some value to this in class B as str = #"NewValue".
This value persists for all methods in class B. But if I access it in class C (after assignment in B) I am getting it as OldValue.
Am I missing something? Should i use extern in other classes?
Thanks & Regards,
Yogini
Update: As of Xcode 8, Objective-C does have class properties. Note, it's mostly syntactic sugar; these properties are not auto-synthesized, so the implementation is basically unchanged from before.
// MyClass.h
#interface MyClass : NSObject
#property( class, copy ) NSString* str;
#end
// MyClass.m
#import "MyClass.h"
#implementation MyClass
static NSString* str;
+ (NSString*) str
{
return str;
}
+ (void) setStr:(NSString*)newStr
{
if( str != newStr ) {
str = [newStr copy];
}
}
#end
// Client code
MyClass.str = #"Some String";
NSLog( #"%#", MyClass.str ); // "Some String"
See WWDC 2016 What's New in LLVM. The class property part starts at around the 5 minute mark.
Original Answer:
Objective-C doesn't have class variables, which is what I think you're looking for. You can kinda fake it with static variables, as you're doing.
I would recommend putting the static NSString in the implementation file of your class, and provide class methods to access/mutate it. Something like this:
// MyClass.h
#interface MyClass : NSObject {
}
+ (NSString*)str;
+ (void)setStr:(NSString*)newStr;
#end
// MyClass.m
#import "MyClass.h"
static NSString* str;
#implementation MyClass
+ (NSString*)str {
return str;
}
+ (void)setStr:(NSString*)newStr {
if (str != newStr) {
[str release];
str = [newStr copy];
}
}
#end
Unlike Java, where a static variable is scoped for all instances of a class, static in C means that a variable is accessible only from within the file where it is declared. It allows you to do things like declare a static variable inside a function, which sets the value only the first time through, like this.
One thing you haven't mentioned is the relationship between classes A, B, and C. If they are in an inheritance hierarchy, and you're expecting the static variable to be inherited as in Java, the method described by zpasternack will work.
If the three classes are unrelated, and you just want to access the value declared in A, then extern is a more appropriate way to go. In this case, you want to declare the variable as extern in ClassA.h, then define it in Class.m. As long as ClassB and ClassC import ClassA.h, they will be able to link against the same extern definition.
One fine point is that, instead of using extern by itself, it's more robust to use OBJC_EXPORT, which is defined in objc-api.h and handles compiling under C++ as well. Here's a code sample:
// ClassA.h
OBJC_EXPORT NSString* commonString;
...
// ClassA.m
NSString* commonString = #"OldValue";
// ClassB.m
#import "ClassA.h"
...
commonString = #"NewValue"; // Can be inside a function or method
Of course, using externed variables in this way creates an infamous, much-maligned global variable, which is fragile in that anyone can read or write it, and access is uncontrolled. This is the simple approach, and answers your question about using static vs. extern. However, as a design principle, the encapsulation provided by wrapping the variable with class methods is much safer, albeit more complex. In object-oriented languages, when the effect you're trying to achieve is that of a class-static method, encapsulation is probably the right way to go.