Objective C - Defining macro to call a method? - objective-c

I want to define a macro to call the following, Is this possible?
I also want it to accept format string.
- (void)logString:(NSString *)string withLogLogLevel:(LogLevel)logLevel
{
// Sav log to file
}
DLog("text");
[Logger logString:text withLogLevel:LogLevelDebug];
ILog("text");
[Logger logString:text withLogLevel:LogLevelInfo];
ELog("text");
[Logger logString:text withLogLevel:LogLevelInfo];

Assuming that logString:withLogLevel: takes a single string parameter in addition to the log level, this should be possible:
#define DLog(x) [Logger logString:(x) withLogLevel:LogLevelDebug]
Note the parentheses around the macro parameter, it is useful when macros are called with composite expressions.
Assuming that the logger takes NSString objects, not C string, you should use the macro like this:
DLog(#"Text");
However, in this case it is not clear why would one prefer a macro to a simple function call:
void DLog(NSString *str) {
[Logger logString:str withLogLevel:LogLevelDebug];
}

Related

How to properly log Vulkan result type strings

Logging VkResult enum values
The VkResult enum contains a lot of values. Unfortunately though, they are C enums which aliases an integer, so I cannot easily just log their names to the console. For this purpose, I am envisioning a function which does something like this:
void graphics::log_vk_result(VkResult result)
{
switch (result)
{
case VK_SUCCESS:
log_core_debug("VK_SUCCESS"); return;
case VK_NOT_READY:
log_core_debug("VK_NOT_READY"); return;
[...]
}
But some of the enum values are only supported by certain extensions, as indicated here. An example: The extension VK_EXT_debug_report introduces the following value to the enumeration: VK_ERROR_VALIDATION_FAILED_EXT. So my idea for (potentially) more portable code would be something like this:
void graphics::log_vk_result(VkResult result)
{
switch (result)
{
[...]
#if defined(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)
case VK_ERROR_VALIDATION_FAILED_EXT:
log_core_debug("VK_ERROR_VALIDATION_FAILED_EXT");
#endif
}
I found this name by looking at the extension manual page. I cannot easily see whether or not VK_EXT_DEBUG_REPORT_EXTENSION_NAME is a macro or an enum - it is a const char* but stated under the section "New Enum Constants". So checking for this particular value, for this particular extension, was my default choice.
[I do realize this extension is marked as deprecated, and I'm not using it! I only use it as an example here.]
I have two questions:
Is this needed?
Is this the correct way of doing this?
Thanks a lot!
All of this is unnecessary, since Vulkan SDK already includes the desired functionality:
#include <vulkan/vk_enum_string_helper.h>
void graphics::log_vk_result( const VkResult result ){
log_core_debug( string_VkResult(result) );
}

Change objective C anonymous function to non anonymous one

I'm a newbie in objective c working on swift.io and trying to convert its anonymous callbacks to non-anonymous function.
basically trying to convert
[self.Socket on:#"connect" callback:^(NSArray* data, SocketAckEmitter* ack) {
NSLog(#"socket connected");
}];
to something like
[self.Socket on:#"connect" callback:connectTestCallback];
i tried defining a following function which i'll call
- (void) connectTestCallback:(NSArray* )data withAck:(SocketAckEmitter *)ack
{
NSLog(#"socket connected");
}
But i'm not sure if thats how i'm supposed to define it, and have no idea how to call it. I've tried calling it as callback:#selector(connectTestCallback) but obviously that didn't work.
Following is definition of "on" function.
- (void)on:(NSString * __nonnull)event callback:(void (^ __nonnull)(NSArray * __nonnull, SocketAckEmitter * __nonnull))callback;
The "anonymous function" is a block building a closure. In contrast to functions and methods a closure stores its creation environment, so you can access this inside the code. But if the actual arguments are enough data to do what you want to do, you can simply send a message to self to execute the "named" method:
[self.Socket on:#"connect" callback:^(NSArray* data, SocketAckEmitter* ack)
{
[self connectTestCallback:data withAck:ack];
}];

Objective-c refer variable outside of block

I'd like to refer variable which I define inside of if block at outside of if else block. How can I? If it can't be, do I have to write same long code (they can't be function or method) inside if block and else block?
if (aTrack.compilation)
{
NSString *artistName = NSLocalizedString(#"compilations", #"");
}
else
{
NSString *artistName = aTrack.artist
}
NSLog(#"%#",artistName);
What #nickfalk and #CRD posted is good but you can also (for such easy statements) use a ternary operator which in this case will look like this:
NSString *artistName = aTrack.compilation ? NSLocalizedString(#"compilations", #""): aTrack.artist;
NSLog(#"%#",artistName);
It is a matter of style but I would go this way for this simple example as my code is in one line
The lifetime of an ordinary (non static) variable declared in a block is just that block, any nested blocks, etc. This is part of the standard lifetime and visibility rules of (Objective-)C(++).
Just declare the variable before the if/else and assign values to it in each block.
This will do what you want:
NSString *artistName;
if (aTrack.compilation){
artistName = NSLocalizedString(#"compilations", #"");
} else {
artistName = aTrack.artist;
}
NSLog(#"%#",artistName);
Also have a look at CRD's reply as this is really basic knowledge and you really need to understand this. (Also, as viperking noticed in my example, there was a terminating semicolon missing in your original code...)
Viperking has a nice example using the the ternary operator. It might be a bit alien at first but is rather nice when you wrap your head around it. a third solution would be
NSString *artistName = aTrack.artist;
if (aTrack.compilation){
artistName = NSLocalizedString(#"compilations", #"");
}
NSLog(#"%#",artistName);
For more complex scenarios I would advice against it, but for an example with two possible cases and one single string it would be quite legible. I'd also advice using nil rather than an empty-string for the localizedString comment.

How to enforce parameters of anonymous blocks to be unused in Objective-C?

I've run into a situation while using a library called TransitionKit (helps you write state machines) where I am want to supply entry and exit actions in the form of a callback.
Sadly, the callbacks include two completely useless parameters. A typical block has to look like this:
^void (TKState *state, TKStateMachine *stateMachine) {
// I TOTALLY don't want parameters `state` or `stateMachine` used here
};
(this is an anonymous code block. Read up on blocks here if you're unclear)
As I've noted in the comment, I really don't want those parameters even mentioned in the body there. I've tried simply removing the parameter names like suggested in this question like so:
^void (TKState *, TKStateMachine *) {
// I foobar all I like here
};
but sadly the code won't compile then :(.
How can I enforce this non-usage of parameters in code?
This is what I could come up with. Quite a hack and relies on the GCC poison pragma, which is not standard but a GNU extension - although, given that you are probably compiling this with clang anyway, it should not be a problem.
#define _state state
#define _stateMachine stateMachine
#pragma GCC poison state stateMachine
Then this compiles:
^(TKState *_state, TKStateMachine *_stateMachine) {
do_something();
}
But this doesn't:
^(TKState *_state, TKStateMachine *_stateMachine) {
do_something(state, stateMachine);
}
You could just have a function that took one kind of block, and returned another, like this:
#class TKState, TKStateMachine; // here so this will compile
typedef void (^LongStateBlock)(TKState *state, TKStateMachine *stateMachine);
static inline LongStateBlock Adapter(void(^block)()) {
void(^heapBlock)() = [block copy]; // forces block to be on heap rather than stack, a one-time expense
LongStateBlock longBlock = ^(TKState *s __unused, TKStateMachine *sm __unused) {
heapBlock();
};
// this is the non-ARC, MRR version; I'll leave ARC for the interested observer
[heapBlock release];
return [[longBlock copy] autorelease];
}
And in practice:
// this represents a library method
- (void)takesLongStateBlock:(LongStateBlock)longBlock
{
// which hopefully wouldn't look exactly like this
if (longBlock) longBlock(nil, nil);
}
- (void)yourRandomMethod
{
[self takesLongStateBlock:^(TKState *state, TKStateMachine *stateMachine) {
NSLog(#"Gratuitous parameters, AAAAHHHH!");
}];
[self takesLongStateBlock:Adapter(^{
NSLog(#"So, so clean.");
})];
}
The whole thing is gisted, and should compile inside any class. It does what you expect when you call -yourRandomMethod.
AFAIK there is no way to do what you want when you are creating a block, you can only miss the parameter names when you are declaring a block variable(a reference to a block, to avoid misunderstandings)
So here you can miss the param names:
void (^myBlock)(SomeClass *);
But not when you create a block:
myBlock = ^(SomeClass *o)
{
};
I'd write
^void (TKState *unused_state, TKStateMachine *unused_stateMachine) {
// Anyone using unused_state or unused_stateMachine gets what they deserve.
};
Of course someone can use the parameters. But then whatever you do, they can change the code. If someone is intent on shooting themselves in the foot, there is no stopping them.

Method for deriving a string from an enum

I've been wondering if there's an elegant way to derive a string from an enum in Objective-C or vanilla C. I'm currently using a switch statement like so:
switch (self.requestType)
{
case MSListRequest:
serverRequestType = #"List";
break;
case MSDetailsRequest:
serverRequestType = #"Details";
break;
case MSPurchaseRequest:
serverRequestType = #"PurchaseVolume";
break;
}
I'm curious if there's a cleaner way to derive strings than this.
-edit:
I'm also using the same enum elsewhere to interface to a different system that needs to map the same enums to a different set of strings.
There's no real nice way to do this. A very simple way is to create an array:
NSString *const ENUM_NAMES[] = {
#"List", #"Details", #"PurchaseVolume", ...
};
There are alternatives that use macros and some simple preprocessor hacks to define both the names and the enum itself from the same source. However, the resulting code is more difficult to read.
// some_enum.def
X(List),
X(Details),
X(PurchaseVolume)
// some_enum.h
enum {
#define X(x) x
#include "some_enum.def"
#undef X
};
// some_enum.c
char const *const ENUM_STRING[] = {
#define X(x) #x
#include "some_enum.def"
#undef X
};
I'm not sure of the best way to generate an NSString from the preprocessor, whether you can just stick an # in it or if it's better to use (NSString *)CFSTR(x).
When I need a bunch of code like this, I write a Python script to generate the code from a text file -- it generates GPerf output for converting strings to enum, and it generates the code for converting enum to string as well. Plain old C doesn't do reflection.