The method I am trying to call is;
- (void)addLogWithLevel:(MDCLogLevel)logLevel logContent:(NSString *)logContent, ...
{
va_list args;
va_start(args, logContent);
NSString *message = [[NSString alloc] initWithFormat:logContent
arguments:args];
va_end(args);
MDCLog *log = [MDCLog logWithContent:message content:logLevel];
[self.deviceLogs addObject:log];
}
I have defined the macro as;
#define MDCLogDebug(format, ...) [[MDCLogController sharedController] addLogWithLevel:MDCLogLevelDebug logContent:(__VA_ARGS__)];
I have tried various formats of this macro, but nothing seems to work.
If I am to call;
MDCLogDebug(#"Test:%#", #"Hey");
All I see in the console is;
Hey
Where am I going wrong? I'm new to using Variadic methods and my C isn't so great!
Actually, your problem is not really related to Objective-C directly, but to C itself, as macros are plain C preprocessor directives.
In a macro, __VA_ARGS__ represents the arguments that are placed instead of the ....
So in your call to MDCLogDebug(#"Test:%#", #"Hey"), the format argument is #"Test:%#" and __VA_ARGS__ represents the rest of the arguments afterwards, namely simply #"Hey" in your case.
If you want to pass both the #"Test:%#" and #"Hey" as arguments to logContent:, you have to explicitly tell it so, using:
#define MDCLogDebug(format, ...) [[MDCLogController sharedController] addLogWithLevel:MDCLogLevelDebug logContent:format, __VA_ARGS__]
Note: An even better solution would be to use the ## prefix before __VA_ARGS__ so that the comma won't be added if __VA_ARGS__ is empty (namely if you only pass a format argument but nothing afterwards, like MDCLogDebug(#"Foo")):
#define MDCLogDebug(format, ...) [[MDCLogController sharedController] \
addLogWithLevel:MDCLogLevelDebug \
logContent:format, ## __VA_ARGS__]
(Note: I use backslashes in this last macro definition above to allow the macro to be written on multiple lines, instead of writing it on one single big long line)
For more information, see the official GCC documentation about Variadic Macros here.
Related
We are currently using the Microsoft.VisualStudio.TestTools.CppUnitTestFramework which defines test classes with a macro, ex:
TEST_CLASS(Class1)
{
public:
TEST_METHOD(Method1)
{
Logger::WriteMessage("In Method1");
Assert::AreEqual(0, 0);
}
};
I am having a hard time getting clang-format to "understand" that TEST_CLASS is a struct/class definition. After trying a couple of things the current best solution I got is to define two new macros to wrap the class:
#define START_TEST(className) TEST_CLASS(className){
#define END_TEST };
and define them as a block start/end:
MacroBlockBegin: ^START_TEST.*$
MacroBlockEnd: ^END_TEST.*$
I am wondering if there is a better solution that does not involve adding more macros...
I have define VLog like this:
#define VLog(s, ...) NSLog(#"%#", [NSString stringWithFormat:(s), ##__VA_ARGS__])
I know VLog(#"hello,%d%#", 1, #"a"); __VA_ARGS__ is replaced with 1, #"a".
Whereas, VLog(#"hello"); __VA_ARGS__ is replaced with what?
and if I define VLog like this:
//delete ##
#define VLog(s, ...) NSLog(#"%#", [NSString stringWithFormat:(s), ##__VA_ARGS__])`
VLog(#"123"); is pointed out error.
In case of zero variable arguments, __VA_ARGS__ is replaced with nothing. ## is special in that it removes preceding , in that case, so compiler won't complain "expression needed after comma". A macro defined without ##, i.e. [NSString stringWithFormat:(s), __VA_ARGS__] will require additional arguments.
I am trying to make some reusable blocks for my application.
CommonBlocks.h
void (^testBlock)(int) = ^(int number) {
// do nothing for now;
};
VariousImplementationFile.m
#import "CommonBlocks.h"
(void)setup {
testBlock(5);
}
Unfortunately, when I try to push this code to iOS device I receive error: linker command failed with exit code 1 (use -v to see invocation). It seems that I missing some.
Any advice?
Thanks
You try add static keyword before the declaration:
static void (^testBlock)(int) = ^(int number) {
// do nothing for now;
};
Your code causes error because you have non-static variable testBlock declared in .h header file.
When you call #import "CommonBlocks.h" in VariousImplementationFile.m, testBlock is declared once. Then you import CommonBlocks.h in some where else, testBlock is declared once more, so you'll get symbol duplicate error.
Declare block in CommonBlocks.h this way
typedef void (^RCCompleteBlockWithResult) (BOOL result, NSError *error);
Then you may use in any method for example:
-(void)getConversationFromServer:(NSInteger)placeId completionBlock:(RCCompleteBlockWithResult)completionBlock
This is not specific to blocks. Basically, you want to know how to have a global variable that is accessible from multiple files.
Basically, the issue is that in in C, each "symbol" can only be "defined" once (it can be "declared" multiple times, but just be "defined" once). Thus, you cannot put the "definition" of a symbol in a header file, because it will be included in multiple source files, so effectively, the same symbol will be "defined" multiple times.
For a function, the prototype is declaration, and the implementation with the code is the definition. You cannot implement a function in a header file for this reason. For a regular variable, writing the name and type of the variable is defining it. To only "declare" it, you need to use extern.
It is also worth mentioning static. static makes a variable local to a particular source file. That way, its name won't interfere with variables with the same name elsewhere. You can use this to make global variables that are "private" to a particular file. However, that is not what you are asking for -- you are asking for the exact opposite -- a variable that is "public", i.e. shared among files.
The standard way to do it is this:
CommonBlocks.h
extern void (^testBlock)(int); // any file can include the declaration
CommonBlocks.m
// but it's only defined in one source file
void (^testBlock)(int) = ^(int number) {
// do nothing for now;
};
There are 3 (which I know) ways to suppress the "unused variable" warning. Any particular way is better than other ?
First
- (void)testString:(NSString *)testString
{
(void)testString;
}
Second
- (void)testString:(NSString *)__unused testString
{
}
Third
- (void)testString:(NSString *)testString
{
#pragma unused(testString)
}
This is the approach I use: cross platform macro for silencing unused variables warning
It allows you to use one macro for any platform (although the definitions may differ, depending on the compiler), so it's a very portable approach to express your intention to popular compilers for C based languages. On GCC and Clang, it is equivalent of wrapping your third example (#pragma unused(testString)) into a macro.
Using the example from the linked answer:
- (void)testString:(NSString *)testString
{
MONUnusedParameter(testString);
}
I've found this approach best for portability and clarity, in use with some pretty large C, C++, ObjC, and ObjC++ codebases.
If you are compiling with GCC, you can take advantage of attribute extensions to set the 'unused' attribute. Like this:
int somevar __attribute__((unused));
It also works for unused parameter warnings (-Wunused-parameter)
To make it shorter to write I am using this macro:
#define _U_ __attribute__((unused))
And declare like this:
int somevar _U_ ;
One way to do it is just to assign a variable pointlessly after it is declared For example:
int foo;
foo = 0;
This should suppress the unused variable warning. It is just a pointless assignment.
But otherwise I would agree with ouah, the first method is the most reliable, if you must choose from those three.
I know this question has been asked several times, but mine is slightly different. Before closing this as duplicate, please read it fully.
There are many posts on stack overflow that says, "Personally, I hate MACROS, Don't use that shit". I've read all those and my case is different. I'm trying to define URLs used in a software (iOS app) using #define macros.
I agree that using const strings is a better practice than #define macros. But in an increasingly REST based API world that accepts query parameters as a part of URL, how can you still use const strings to represent a URL that changes?
Instead of http://api.myblog.com/posts?entryid=%#
a API Server that following REST principles would have
http://api.blog.com/posts/entries/[entryid]
In the former type, URL is http://api.myblog.com/posts for all entries and they don't change. A const string was possible.
In the latter type, URL changes with every entry and I use a Macro that expands to a full URL like this.
#define GET_ENTRY_URL(__MY_ENTRY_ID__) [NSString stringWithFormat:#"http://api.myblog.com/posts/entries/%#", __MY_ENTRY_ID__];
Are there any design flaws in my method?
Would like to know your inputs.
Thanks.
Looking from the perspective of the compiler, #define is a preprocessor directive (refer to the definition in c, http://en.wikipedia.org/wiki/C_preprocessor).
In this case, compiler might be doing the whole text replacement before compiling your codes.
e.g.: if you define:
#define GET_ENTRY_URL(__MY_ENTRY_ID__) [NSString stringWithFormat:#"http://api.myblog.com/posts/entries/%#", __MY_ENTRY_ID__];
it could be replacing every occurrences of GET_ENTRY_URL(x) with [NSString ..., x] in your codes. Potentially, instances might be created everywhere we use the macro if the implementation of objective-c is following this.
static const/variable seems to be a better way.
What I did in my app was define a const for the base path and a const for each specific path with substitution format codes inside the path when necessary.
NSString const *APIBasePath = #"http://api.mydomain.com";
NSString const *APIEntryPath = #"/entries/%d";
NSString const *APIUpdateEntryPath = #"/entries/%d/update";
I then construct the URL at runtime for each API as follows:
- (void)updateEntryNumber:(NSUInteger)entryNumber
{
NSString *updateEntryPath = [NSString stringWithFormat:APIUpdateEntryPath, entryNumber];
NSString *APIPath = [APIBasePath stringByAppendingPathComponent:updateEntryPath];
// do something
}