I'm developing a static library that will be distributed to other developers, who may need debug statements. So I have several levels of logging.
In order to avoid constant appearance of
if(loggingLevelCurrentlySet >= loggingLevelWantedForThisInstance){
NSLog(#"log this");
}
I created a set of logging function wrappers. A simplified version looks like this:
void myLog(int logLevel, NSString *format, va_list args){
if((loggingLevelCurrentlySet >= logLevel)){
NSLogv(format, args);
}
}
void myLogLevel1(NSString *format, ...){
va_list args;
va_start(args, format);
myLog(1, format, args);
va_end(args);
}
void myLogLevel2(NSString *format, ...){
va_list args;
va_start(args, format);
myLog(2, format, args);
va_end(args);
}
etc.
But now, I want, from within myLog, access to the fully formated string to do something else with.
void myLog(int logLevel, NSString *format, va_list args){
NSString *fullString = [NSString stringWithFormat:format, args]; //crashes when args is anything but an empty list
CFStringRef cfsr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, format, args); //also crashes
//want to use the string here
if((loggingLevelCurrentlySet >= logLevel)){
NSLogv(format, args);
}
}
NSString *fullString = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
There is a method for that ;)
Although I suggest not to use functions, but some simple macro definitions:
#define myLogLevel1(format, ...) myLog(1, format, __VA_ARGS__)
#define myLogLevel2(format, ...) myLog(2, format, __VA_ARGS__)
Related
I'm using C++Builder 10.4.2 and having a problem with qsort. I rarely use qsort so I might be making a clumsy mistake. Array 'buffer' is a 2D 'char' array with more than 26,000 rows of single words.
This is the call:
qsort((void *)buffer,wordcount,sizeof(buffer[1]),sort_function);
This is the compare function:
int TForm::sort_function(const void *a, const void *b)
{
return( strcmp((char *)a,(char *)b) );
}
This is the error message. Notice that it's complaining about sort_function for 4th argument:
search.h(46): candidate function not viable: no known conversion from 'int (__closure *)(const void *, const void *)' to 'int (*)(const void *, const void *) __attribute__((cdecl))'
What is 'int (__closure *)'? Is there a way to fix my compare function?
__closure is a Borland compiler extension for obtaining a pointer to a non-static class method, without regard to the type of class being used. This is most commonly used in VCL/FMX components, which allow you to assign event handlers from any class you want, which is not something that standard C++ typically allows you to do.
qsort() expects a C-style function pointer in the 4th parameter. You can't get such a pointer to a non-static class method.
To solve this, you need to use either:
a standalone function
a static class method
a non-capturing C++ lambda (C++11 or higher only)
Since your sort_function() does not need access to your TForm object, declaring sort_function() as static would be the simplest fix:
// .h
class TForm
{
...
private:
static int sort_function(const void *a, const void *b);
void doSomething();
...
};
// .cpp
int TForm::sort_function(const void *a, const void *b)
{
return strcmp((const char *)a, (const char *)b);
}
void TForm::doSomething()
{
...
qsort(buffer, wordcount, sizeof(buffer[1]), sort_function);
...
}
However, it really should be a standalone function instead since it really has no relation to your TForm class at all:
// .cpp
static int sort_function(const void *a, const void *b)
{
return strcmp((const char *)a, (const char *)b);
}
void TForm::doSomething()
{
...
qsort(buffer, wordcount, sizeof(buffer[1]), sort_function);
...
}
How do I define Apple's NSLOG prints from C code ?
For Android it would be
#if _ANDROID__
# include <android/log.h>
# define LOGFUNC(level, fmt, args) __android_log_vprint(level, "andorid", fmt, args)
but How to to do it with Apple and NSLOG ?
I know I can do something like
#elif __APPLE__
# define LOGFUNC(level, fmt, args) vprintf(fmt, args)
but I dont see the logs in the device Logs.
You can provide a C-stub implemented as Objective-C and compile that for OSX/iOS only, providing equivalents for Android, Windows, etc. That's what I normally do when writing cross-platform code.
Log.h:
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
extern void logMsg(const char *fmt, ...);
#ifdef __cplusplus
}
#endif
AppleLog.m:
#import <Foundation/Foundation.h>
#import "Log.h"
void logMsg(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
NSString *message = [[NSString alloc] initWithFormat:#(fmt) arguments:va];
va_end(va);
NSLog(#"%#", message);
}
You can also provide platform-specific code for getHomeDirectory(), getTempDirectory(), etc. in much the same way.
I need to obtain MCC and MNC code for the current country (NOT from the class CTCarrier for the SIM home country).
I use private API for CoreTelephony.framework. On the my device all works correct. But on the other devices in the method CellMonitorCallback we obtain cells = NULL.
May be somebody can help what I done wrong?
#import "AMCoreTelephone.h"
#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
struct CTResult
{
int flag;
int a;
};
extern CFStringRef const kCTCellMonitorCellType;
extern CFStringRef const kCTCellMonitorCellTypeServing;
extern CFStringRef const kCTCellMonitorCellTypeNeighbor;
extern CFStringRef const kCTCellMonitorCellId;
extern CFStringRef const kCTCellMonitorLAC;
extern CFStringRef const kCTCellMonitorMCC;
extern CFStringRef const kCTCellMonitorMNC;
extern CFStringRef const kCTCellMonitorUpdateNotification;
id _CTServerConnectionCreate(CFAllocatorRef, void*, int*);
void _CTServerConnectionAddToRunLoop(id, CFRunLoopRef, CFStringRef);
mach_port_t _CTServerConnectionGetPort(id);
#ifdef __LP64__
void _CTServerConnectionRegisterCallService(id);
void _CTServerConnectionUnregisterCallService(id,int*);
void _CTServerConnectionRegisterForNotification(id, CFStringRef);
void _CTServerConnectionCellMonitorStart(id);
void _CTServerConnectionCellMonitorStop(id);
void _CTServerConnectionCellMonitorCopyCellInfo(id, void*, CFArrayRef*);
void _CTServerConnectionIsInHomeCountry(id, void*, int*);
void _CTServerConnectionCopyCountryCode(id, void*, CFStringRef);
#else
void _CTServerConnectionRegisterCallService(struct CTResult*, id);
#define _CTServerConnectionRegisterCallService(connection) { struct CTResult res; _CTServerConnectionRegisterCallService(&res, connection); }
void _CTServerConnectionRegisterForNotification(struct CTResult*, id, CFStringRef);
#define _CTServerConnectionRegisterForNotification(connection, notification) { struct CTResult res; _CTServerConnectionRegisterForNotification(&res, connection, notification); }
void _CTServerConnectionCellMonitorStart(struct CTResult*, id);
#define _CTServerConnectionCellMonitorStart(connection) { struct CTResult res; _CTServerConnectionCellMonitorStart(&res, connection); }
void _CTServerConnectionCellMonitorStop(struct CTResult*, id);
#define _CTServerConnectionCellMonitorStop(connection) { struct CTResult res; _CTServerConnectionCellMonitorStop(&res, connection); }
void _CTServerConnectionCellMonitorCopyCellInfo(struct CTResult*, id, void*, CFArrayRef*);
#define _CTServerConnectionCellMonitorCopyCellInfo(connection, tmp, cells) { struct CTResult res; _CTServerConnectionCellMonitorCopyCellInfo(&res, connection, tmp, cells); }
void _CTServerConnectionIsInHomeCountry(struct CTResult*, id, int*);
#define CTServerConnectionIsInHomeCountry(connection, isHomeCountry) { struct CTResult res; _CTServerConnectionIsInHomeCountry(&res, connection, &isHomeCountry); }
#endif
#implementation AMCoreTelephone
{
CTCarrier *_carrier;
id CTConnection;
mach_port_t port;
}
+ (instancetype) sharedInstance
{
static AMCoreTelephone *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[AMCoreTelephone alloc] init_true];
});
return instance;
}
- (instancetype) init_true
{
if (self = [super init]) {
_carrier = [[CTTelephonyNetworkInfo new] subscriberCellularProvider];
}
return self;
}
- (void) startMonitoring{
#if TARGET_IPHONE_SIMULATOR
return;
#else
CTConnection = _CTServerConnectionCreate(kCFAllocatorDefault, CellMonitorCallback, NULL);
_CTServerConnectionRegisterForNotification(CTConnection, kCTCellMonitorUpdateNotification);
port = _CTServerConnectionGetPort(CTConnection);
CFMachPortRef ref = CFMachPortCreateWithPort(kCFAllocatorDefault,port,NULL,NULL, NULL);
CFRunLoopSourceRef rlref = CFMachPortCreateRunLoopSource ( kCFAllocatorDefault, ref, 0);
CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
CFRunLoopAddSource(currentRunLoop, rlref, kCFRunLoopCommonModes);
_CTServerConnectionCellMonitorStart(CTConnection);
#endif
}
- (void) stopMonitoring{
_CTServerConnectionCellMonitorStop(CTConnection);
}
int CellMonitorCallback(id connection, CFStringRef string, CFDictionaryRef dictionary, void *data)
{
int tmp = 0;
CFArrayRef cells = NULL;
_CTServerConnectionCellMonitorCopyCellInfo(connection, (void*)&tmp, &cells);
if (cells == NULL)
{
return 0;
}
for (NSDictionary* cell in (__bridge NSArray*)cells)
{
int LAC, CID, MCC, MNC;
if ([cell[(__bridge NSString*)kCTCellMonitorCellType] isEqualToString:(__bridge NSString*)kCTCellMonitorCellTypeServing])
{
LAC = [cell[(__bridge NSString*)kCTCellMonitorLAC] intValue];
CID = [cell[(__bridge NSString*)kCTCellMonitorCellId] intValue];
MCC = [cell[(__bridge NSString*)kCTCellMonitorMCC] intValue];
MNC = [cell[(__bridge NSString*)kCTCellMonitorMNC] intValue];
}
else if ([cell[(__bridge NSString*)kCTCellMonitorCellType] isEqualToString:(__bridge NSString*)kCTCellMonitorCellTypeNeighbor])
{
}
}
CFRelease(cells);
return 0;
}
#end
I think the problem is using private api, so you can't run your app on non jailbreak phones. I'm researching the thing as same you but a little late :) and I found this answer to work on iOS 8.3, it says
As of iOS 8.3 all of the above solutions require entitlement to work
<key>com.apple.CommCenter.fine-grained</key>
<array>
<string>spi</string>
</array>
Also this project on github is only sample code for me that I can find.
I think you already know answer but this may helps someone else, because it's hard to find :)
As of iOS 8.3 all of the above solutions require entitlement to work
<key>com.apple.CommCenter.fine-grained</key>
<array>
<string>spi</string>
</array>
Indeed, tha above code mentioned is said that can be run to get the lac and cell on ios 8.3 and above. But I really don't know how to insert the above on a jailbroken phone. Could anyone give any detail information. Or anyone test this way ?
I am currently having issues converting NSString to a const void *
Here is my code:
DoSomethingFunction:(const void *)parameter
[class DoSomeThingFunction:(const void *)passswordField.text]
Password field is a UITextfield. The value becomes null when I try and cast it.
I want passwordField.text to be a const void * so it can be used in the function.
It depends on function implementation. It can be like this:
NSString *string = #"text";
const void *parameter = CFBridgingRetain(string);
DoSomethingFunction(parameter);
If function has similar parameter handling
void DoSomethingFunction(const void *parameter) {
NSString *string = CFBridgingRelease(parameter);
NSLog(#"%#", string);
}
Try casting the result of one of the NSString's methods that return a C string:
– cStringUsingEncoding:
– getCString:maxLength:encoding:
– UTF8String
NSString Class Reference
My environment: Xcode5, iOS, Objective-C/Objective-C++ mix.
I am trying to figure out what causes the next problem. I am writing my own logging function:
int _me_log(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
char *c = va_arg(args, char *);
char *message = NULL;
printf(fmt, args);
int n = asprintf(&message, fmt, args);
if (n != -1 && message != NULL) {
//do something with 'message' like writing to file, etc.
UPDATE:
//we need to handle memory created for 'message' storage.
free(message);
}
va_end(args);
return n;
}
Then I call it like this:
_me_log("socket %s did open", "Socket: 0x1fd1c880");
And instead of correct output socket Socket: 0x1fd1c880 did open I get some gibberish like this socket \\323\331/ did open in this line printf(fmt, args);.
If I call it this way printf("%s", c); I get correct results.
I have googled several implementations (this or this ) of logging functions and functions which pass variable parameters and it seems that I do everything correctly.
Could you please suggest me what I'm doing wrong?
You've got the right idea to use va_list here, but if you work with va_list you should use vasprintf instead of asprintf:
int _me_log(const char *fmt, ...)
{
va_list args;
char *message = NULL;
int n;
va_start(args, fmt);
n = vasprintf(&message, fmt, args);
if (n != -1 && message != NULL) {
// ... use message ...
}
free(message);
va_end(args);
return n;
}
For every routine of the printf family, there is a variant that takes a va_list instead of the variadic argument ... and whose name is prefixed with the letter v, for example:
int printf(const char *format, ...);
int vprintf(const char *format, va_list ap);
These routines exist so you can write you own (non-macro) wrapper for xprintf.
Seems like a very complicated implementation. Try:
int _me_log(const char *fmt, ...) {
int ret = 0;
va_list va;
va_start(va, fmt);
ret = vprintf(fmt, va);
va_end(va);
putc('\n', stdout);
return ret;
}
But, of course, that is no different from printf(), except for forcing a newline.