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
}
Related
There are many questions about declaring const string in .h files, this is not my case.
I need string (for serialization purposes if it is important) to use in
My current solution is
// file.cpp
static constexpr const char* const str = "some string key";
void MyClass::serialize()
{
// using str
}
void MyClass::deserialize()
{
// using str
}
Does it have any problems? (i.e. memory leaks, redefinitions, UB, side effects)?
P.S. is using #define KEY "key" could be better here (speed/memory/consistency)?
Since you mentioned C++17, the best way to do this is with:
constexpr std::string_view str = "some string key";
str will be substituted by the compiler to the places where it is used at compile time.
Memory-wise you got rid of storing the str in run-time since it is only available at compile time.
Speed-wise this is also marginally better because less indirections to get the data in runtime.
Consistency-wise it is also even better since constexpr is solely used for expressions that are immutable and available at compile time. Also string_view is solely used for immutable strings so you are using the exact data type needed for you.
constexpr implies the latter const, which in turn implies the static (for a namespace-scope variable). Aside from that redundancy, this is fine.
I am trying to init a const variable differently based on another const string.
Code is not inside a class, just plain dart.
Only way I found is using the elvis operator but it's quite ugly and will become unmaintanable with many conditions to handle ...
How would you do it ?
test.dart called with --dart-define CONTEXT=context-A:
// can be : "context-A" or "context-B" or "context-C" etc ...
const contextString = String.fromEnvironment('CONTEXT');
const Context contextObject = (contextString == 'context-A')
? ContextA()
: (contextString == 'context-B')
? ContextB()
: ContextC();
Any other method (like calling an init method) fails with dart telling me that I cannot init a const variable with a non const method :(
Thanks
Personally I would use the ternary conditional operator as you're already using; I don't think it's unreadable, and as long as you don't have too many cases (which itself would be a maintenance problem), the indentation creep shouldn't be too bad.
However, one alternative would be to abuse collection-if:
const contextObject = [
if (contextString == 'context-A')
ContextA()
else if (contextString == 'context-B')
ContextB()
else
ContextC()
];
which is formatted more nicely (as long as the expression is sufficiently long that dart format doesn't try to squeeze it all onto a single line). However, I don't recommend this because:
You'll need to some extra overhead of using operator [] everywhere to access the intended object.
Equality comparisons are potential pitfall. contextObject == const [ContextA()] will work, but if const is accidentally omitted, it will never compare equal.
(At some point, perhaps if-expressions will be added to Dart.)
I would like to define a keyword_table which maps some strings to some tokens, and I would like to make this table visible for both parser.mly and lexer.mll.
It seems that the table has to be defined in parser.mly,
%{
open Utility (* where hash_table is defined to make a table from a list *)
let keyword_table = hash_table [
"Call", CALL; "Case", CASE; "Close", CLOSE; "Const", CONST;
"Declare", DECLARE; "DefBool", DEFBOOL; "DefByte", DEFBYTE ]
%}
However, I could NOT use it in lexer.mll, for instance
{
open Parser
let x = keyword_table (* doesn't work *)
let x = Parser.keyword_table (* doesn't work *)
let x = Parsing.keyword_table (* doesn't work *)
}
As this comment suggests, menhir has a solution for this, could anyone tell me any details?
The first option is to define tokens in a separate .mly file. Executing menhir for this file with --only-tokens option will generate a module containing type token that you can use in your parser compiled with --external-tokens option.
If this solves the problem with tokens, you can specify all other functions that are used by both parser and lexer in a separate file as Thomash suggested.
There is an alternative solution as well. You can use %parameter<module signature> declaration in the parser to parametrize the entire parser over type and function annotations specified inside given signature. The main advantage is that this signature is provided in the interface file for the parser, so the parser can share this signature with other modules (that can construct modules based on the signature).
I suggest to refer to menhir examples, namely see calc-two to get know about external tokens and to calc-param to know how to create parametrized parsers.
I usually put the keyword_tablein lexer.mll and I see no reason to put it in parser.mly.
If you need to access it from both lexer.mll and parser.mly (but why do you want to access it from parser.mly?), the easiest solution is to put it in a third file keyword.ml and use Keyword.keyword_table (or open Keyword and keyword_table).
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.
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.