I'm trying to make native (C++) cross-platform logging for desktop and Android. For that I made an abstract native Logger class, and the appropriate inherited classes (StdoutLogger, AndroidLogger, etc.) with the implemented log methods.
So since what Android supports for native logging is the __android_log_print(int prio, const char *tag, const char *fmt, ...) method which works with a printf-like syntax with indefinite number of arguments, I made the abstract log method to work with a similar syntax:
virtual void log(int aLogLevel, const char *tag, const char *format, ...)=0;
Well, for passing these indefinite numbers of arguments to the Android logging method I found I need to use another method which does the same but takes a va_list instead of the indefinite number of parameters. It also exists in log.h and is called __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap), so I just have to pass the arguments for that method instead.
The problem is that for all this to work I need stdarg.h which contains all the stuff I need for this (like the declaration for va_list, etc.), but by default Eclipse can't find it:
Unresolved inclusion:
In Eclipse project settings my include directories include:
${NDKROOT}/platforms/android-9/arch-arm/usr/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include
It's pretty strange that it's missing, because stdarg.h is part of the C standard library.
So I searched and found it here so I added it to the include directories:
${NDKROOT}/toolchains/arm-linux-androideabi-4.6/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.6/include
After this I got it working nicely, but I'm unsure if this is the correct way to do it.
Anyway, later (when implementing the log method in another inherited class) I ran into another problem regarding vfprintf method:
virtual void Log(int aLogLevel, const char *tag, const char *format, ...)
{
if (aLogLevel >= loglevel)
{
va_list args;
va_start (args, format);
vfprintf (stdout, format, args);
va_end (args);
}
}
Eclipse indicated an error:
Invalid arguments ' Candidates are: int vfprintf(__sFILE *, const char *, char *) '
I checked and apparently it wanted a __va_list (with __ at the beginning), not a va_list and it looked like they apparently weren't compatible (according to Eclipse). This vfprintf was in stdio.h, so I started searching and found another stdio.h in a similar folder I found stdarg.h, so I added it to include directories too (and moved it to top) :
${NDKROOT}/toolchains/arm-linux-androideabi-4.6/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.6/include-fixed
Now all errors are gone and everything seem to be working, but I'm really unsure if this was really the correct way to do it.
I have latest Eclipse/CDT/NDK/etc.
Any help would be appreciated.
Edit:
fadden: Thanks for the answer. This comment would have been too long so I put it here.
Yes, I use ${NDKROOT}/ndk-build.cmd to build the native part.
Yeah, I noticed cases when Eclipse would indicate errors so it wouldn't even let me start to build the app, but when I restart Eclipse without opening the file where the error was, it actually builds successfully. But when I open the file which it thinks has errors, it wouldn't let me start to build again. So it really looks like there's quite some inconsistency between errors indicated by Eclipse and actual errors (preventing building). Maybe I should just find a way to let it build regardless of indicated errors, though it could be annoying having errors in the project that should be just ignored, not knowing what are real and what are not so real errors... Or just add everything that is needed to include directories to keep Eclipse happy, so far it seems to be working, just wasn't sure if this was really the right way to do it. Thanks for the help.
Making Eclipse + CDT happy with Android is largely a matter of trial and error. (Days or perhaps weeks have been irrevocably lost trying to get size_t to work right.) Ultimately you should be building with the NDK toolchain, not Eclipse, so as long as Eclipse seems to be happy you're probably in good shape because ultimately it won't affect your binaries.
As you've noticed, some of the headers (like stdarg.h) are provided by gcc, not bionic, so it is necessary to dig around a bit. I expect there's a define somewhere that equates __va_list to va_list if you have the right #defines set (maybe __need___va_list??).
Related
In Typescript (or JavaScript) I always try to write in a way that if I (or another developer) has to touch my code in one year, it is really easy to understand what is happening. So I do not try to find the shortest code possible but the clearer one.
I do not worry about the size of the file because I know in production this function:
function myFunction(value: number) {
if(otherFunction(number){
return true;
}
if(yetAtherFunction(number){
return true;
}
return false;
}
will be converted to this:
function myFunction(n){return!!otherFunction(n)||!!yetAtherFunction(n)}
Would something similar happens with kotlin?
I ask because I offen find this kind of code:
val myDrawable = item?.image?.let { Uri.parse(it.toString()) } ?: R.drawable.my_default_image
and to me it is not easy to do a fast parse to know what is happening why doing a PR or similar.
If I write that in a more verbose way, would it have an impact on the size of the final apk ?
Important:
To clarify, I am not asking is it better to write in this or that way? I am asking if the compiler tries to optimize the input like a typescript/javascript minifier does.
In Kotlin / Java world, all code must get compiled into bytecode before it can run anywhere, and in general this is basically an incredibly optimized binary blob where whitespace doesn't exist.
In interpreted languages like JS, the client / browser downloads a copy of the source and runs the source directly. Minifying is super important in these cases because it reduces the size of the file clients need to download by removing logically redundant characters. In TS, most clients cannot run it directly, so it instead gets transpiled into JS, and that is what is typically served to browsers / clients. (Some exceptions like Deno exist for example, which has a native ts interpreter).
The reason you see inlined code stuffed into one line, is purely for cosmetic / code style purposes.
Additional whitespace and variable names generally have no impact on the size / performance of your compiled Android app, so you can simply write code in the way that seems most presentable to you.
I've successfully written a go mobile library and used it from an Android Java app.
I've also successfully called a c function from JNI. This one has the JNIEnv pointer.
I'd like to pass the context/JNIEnv from Java to Go.
Here's where it seems to get complicated:
Go Mobile does not seem to use JNIEnv at all.
LoadJNI.java does have a context object and claims to pass it to the Go side. But there it isn't visible.
I've been able to include jni.h using the "C" directive
I can transfer the JNIEnv from JNI/C or store it there as a global variable but that's an ugly kludge.
https://github.com/golang/go/issues/16876 talks about a proposal to do reverse binding but it's unclear what the status is.
https://godoc.org/golang.org/x/mobile/cmd/gobind also talks about it but the following code snippet
import "Java/java/lang/System"
t := System.CurrentTimeMillis()
returns
cannot find package "Java/java/lang/System" in any of:
/usr/local/go/src/Java/java/lang/System (from $GOROOT)
/Users/----/go/src/Java/java/lang/System (from $GOPATH)
In C I solved it with a compiler directive: #cgo CFLAGS: -I...
But I'm not sure how to do it for Java
In short, I'm now stuck and would appreciate help. Ideally, I'd just like to pass the object as a parameter, if possible. However, I'd settle for any reverse binding to call Java from Go.
I hate to answer my own question, but I'd hate someone to go through this as well too. I'll update this answer as I go along.
Basic Setup
Edit build.gradle and add GOPATH and GO to gobind (additions in bold):
gobind {
pkg = ".../reverse/reverse"
GOPATH = "/Users/johndoe/go"
GO = "/usr/local/go/bin"
}
How to develop
Start from the reverse example. Get it to compile and run.
Make one modification and recompile.
Missing gobind
It turns out the installation process neglected to build gobind. Run go install golang.org/x/mobile/cmd/gobind
I'm Stuck
Trying to modify the reverse project to do anything other than what it's already doing is failing. Even stuff like passing an object and calling toString will fail.
More to come...
We're having a problem struct memory packing and alignment.
Android is not honoring the #pragma pack(push, <n>) which is in several hundred places in our code base. This is causes segfault.
The Android Clang compiler requires an __ attribute __ decorator on the struct or class, for example:
struct __attribute__((packed, aligned(8))) Test
{
char a;
char b;
double d;
};
As opposed to this for Visual C++ that honors the pragma:
#pragma pack(push, 8)
struct Test
{
char a;
char b;
double d;
};
#pragma pack(pop)
Since the use of #pragma pack is so widespread, it will be a time-consuming task to fix.
We tried using the -mms-bitfields compiler flag which sets the default structure layout to be compatible with the Microsoft compiler standard (i.e. it honors the #pragma pack). However this only works for trivial structs and not classes with base classes or virtual functions. We get the following error with these type of classes.
“error : ms_struct may not produce Microsoft-compatible layouts for classes with base classes or virtual functions [-Wincompatible-ms-struct]”
How can we mitigate this issue - is there any workaround to make #pragma pack work for non-trivial structs/classes other than to go over all the classes/struct between push and pop pragmas and add the packed attribute?
Thanks
First of all, I have the impression, that you are doing something fundamentally wrong, when you have "several hundred places" in your code, where you need to define alignment to prevent a segfault. This pragma is non standard and it is not widespread to use it. Most noticeably it is not widespread to use it as extensive as you do. It is not in the standard as well.
Anyway, since clang will ignore the pragma and msvcc will ignore the attributes, I'd put both in the code. You might use e.g. grep and sed to prevent a lot of manual work.
I was happy using arc4random_uniform since iOS, and also for iOS targets of cocos2d-x.
Turns out it doesn't work for Android. The error:
error: 'arc4random_uniform' was not declared in this scope
How can I get around this?
Worst case, at compile time I'd check if arc4random_uniform() exists, and if not, use some other method (like the old arc4random()...).
I really would like to avoid using different code bases for different targets here.
Any other suggestions?
Note: As cocos2d-x is a "one code"→"many platforms", delegating this issue to Java code for Android would work against that.
Some of the C++ libs you can use in ios are not available in Android.
Unfortunately arc4ramndom is just one of them.
So the only way is to use an alternative of the stdlib like std::rand() or the default random engine if you want something more.
This is an example about how to use std::default_random_engine to get a random value in a given interval.
int randomValue(int from, int to) {
std::random_device rd;
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> uniform_dist(from, to);
int mean = uniform_dist(e1);
return mean;
}
You can use Cocos2d native methods for generating random numbers. CCRANDOM_0_1() for example generates a random CGFloat between 0 and 1.
I'm having troubles with <stdint.h> when using -std=c++0x in GCC 4.4.3 (for Android):
// using -std=c++0x
#include <stdint.h>
uint64_t value; // error: 'uint64_t' does not name a type
But using -std=gnu++0x works:
// using -std=gnu++0x
#include <stdint.h>
uint64_t value; // OK
Is <stdint.h> incompatible with C++0x?
So far as I can tell, I think this could be argued an implementation bug (or actually, since C++0x isn't published, not a bug per se but an incomplete implementation of the current state of the upcoming standard).
Here's why, referring to n3225 for the expected behavior of -std=c++0x:
D.7 says
Every C header, each of which has a
name of the form name.h, behaves as if
each name placed in the standard
library namespace by the corresponding
cname header is placed within the
global namespace scope
OK, so far so easy. What does <cstdint> place in the standard library namespace?
18.4.1:
typedef unsigned integer type uint64_t; // optional
How optional? 18.4.1/2:
The header defines all functions,
types, and macros the same as 7.18 in
the C standard
Drat. What does the C standard say? Taking out n1256, 7.18.1.1/3:
These types are optional. However,
if an implementation provides integer
types with widths of 8, 16, 32, or 64
bits, no padding bits, and (for the
signed types) that have a
two's complement representation, it
shall define the corresponding typedef
names
But hang on, surely on Android with -std=c++0x GCC does provide a 64 bit unsigned type with no padding bits: unsigned long long. So <cstdint> is required to provide std::uint64_t and hence stdint.h is required to provide uint64_t in the global namespace.
Go on, someone tell me why I'm wrong :-) One possibility is that C++0x refers to "ISO/IEC 9899:1999 Programming languages — C" without specifying a version. Can it really be that (a) 7.18.1.1/3 was added in one of the TCs, and also (b) C++0x intends to reference the original standard as of 1999, not the amendments since then? I doubt either of these is the case, but I don't have the original C99 on hand to check (a) and I'm not even sure how to check (b).
Edit: oh, as for which one should be used -std=c++0x isn't really a strict standards-compliant mode yet, since there isn't a strict standard yet. And even if there was a standard, gcc 4.4.3 certainly isn't a finished implementation of it. So I see no great need to use it if -std=gnu++0x is actually more complete, at least in this respect for your combination of gcc version and platform.
However, gnu++0x will enable other GNU extensions, that you might not want your code to use. If you're aiming to write portable C++0x, then eventually you'd want to switch to -std=c++0x. But I don't think GCC 4.4 or any other C++0x implementation-in-progress is complete enough yet for it to be practical to write code from the (draft) standard, such that you could say with a straight face "I'm programming C++0x, and it's only 2011!". So I'd say, use whichever one works, and understand that whichever one you use now, you'll probably be switching to -std=c++11 eventually anyway.