Is "std::cout" usable in Android-ndk
Asked Answered
W

6

46

In Android-ndk, we could use "__android_log_write", "__android_log_print", ... etc to output messages to the "LogCat" window. How about if I use "std::cout" to output some strings ? E.g.

std::cout << "some strings" << std::endl;

Where would the strings be sent.

It seems that Android does not have Console Applications and the above strings may not be sent. Could I redirect the "stdout" to a file so that sending strings to "std::cout" is equivalent to logging messages ?

Warring answered 15/1, 2012 at 13:59 Comment(2)
subset: redirect stdout to logcat: #10531550Chyle
Possible duplicate of C/C++ printfs - Where's it appears in a Android native code?Wellfed
K
33

According to the Android documentation, stdout & stderr output to /dev/null. You can use the Android Debug Bridge to achieve what you want.

By default, the Android system sends stdout and stderr (System.out and System.err) output to /dev/null. In processes that run the Dalvik VM, you can have the system write a copy of the output to the log file. In this case, the system writes the messages to the log using the log tags stdout and stderr, both with priority I. To route the output in this way, you stop a running emulator/device instance and then use the shell command setprop to enable the redirection of output. Here's how you do it:

$ adb shell stop
$ adb shell setprop log.redirect-stdio true
$ adb shell start

The system retains this setting until you terminate the emulator/device instance. To use the setting as a default on the emulator/device instance, you can add an entry to /data/local.prop on the device.

Kaceykachina answered 15/1, 2012 at 14:11 Comment(1)
Tried this. It is not working. /dev/null is empty alwaysSecondrate
V
41

You can create a class derived from std::streambuf which uses the Android specific functions to send the produced sequence of characters. I don't know where the default implementation of std::cout sends characters on Android, however. Basically, this would look something like this:

class androidbuf : public std::streambuf {
public:
    enum { bufsize = 128 }; // ... or some other suitable buffer size
    androidbuf() { this->setp(buffer, buffer + bufsize - 1); }

private:
    int overflow(int c)
    {
        if (c == traits_type::eof()) {
            *this->pptr() = traits_type::to_char_type(c);
            this->sbumpc();
        }
        return this->sync()? traits_type::eof(): traits_type::not_eof(c);
    }

    int sync()
    {
        int rc = 0;
        if (this->pbase() != this->pptr()) {
            char writebuf[bufsize+1];
            memcpy(writebuf, this->pbase(), this->pptr() - this->pbase());
            writebuf[this->pptr() - this->pbase()] = '\0';

            rc = __android_log_write(ANDROID_LOG_INFO, "std", writebuf) > 0;
            this->setp(buffer, buffer + bufsize - 1);
        }
        return rc;
    }

    char buffer[bufsize];
};

To actually set up std::cout to write to this stream buffer, you would do something like this in your main() function:

int main() {
    std::cout.rdbuf(new androidbuf);
    ...
}

This create a memory leak for the one androidbuf stream which is, however, somewhat intentional: the stream may be written to after main() is exited and it is flushed when when std::cout gets destroyed. If you don't want this, you could either restore std::cout's original stream buffer or set it to null and delete the return from rdbuf():

   // avoid a one-time resource leak but don't get output afterwards:
   delete std::cout.rdbuf(0);
Valeriavalerian answered 15/1, 2012 at 14:13 Comment(7)
Thanks for another solution. But it involves more coding, I'll try it later.Warring
Thank you, it's works. But it has several mistakes. Here is fixed version: gist.github.com/dzhioev/6127982Meridethmeridian
Thanks! I modified the code to actually work by copy&paste, hope it might help someone. (might be pending peer review)Pelvic
Wow, you just made my life 100% easier! I wish I could give you more than an upvoteGobble
For me the code only displays the log when the buffer is full.Shikoku
@pvallet: Yes, that is expected unless the stream is explicitly flushed, e.g., using out << std::flush. Sending character to external destinations is generally rather expensive and is normally minimized. You can disable the buffer in androidbuf causing overflow() to be called for each character (also introducing some extra overhead) and call sync() upon specific characters, e.g., '\n'. However, I'd stick with flushing the stream explicitly.Jagannath
If someone is interested in flushing the buffer after every output, try this: std::cout.setf(std::ios::unitbuf);Lives
K
33

According to the Android documentation, stdout & stderr output to /dev/null. You can use the Android Debug Bridge to achieve what you want.

By default, the Android system sends stdout and stderr (System.out and System.err) output to /dev/null. In processes that run the Dalvik VM, you can have the system write a copy of the output to the log file. In this case, the system writes the messages to the log using the log tags stdout and stderr, both with priority I. To route the output in this way, you stop a running emulator/device instance and then use the shell command setprop to enable the redirection of output. Here's how you do it:

$ adb shell stop
$ adb shell setprop log.redirect-stdio true
$ adb shell start

The system retains this setting until you terminate the emulator/device instance. To use the setting as a default on the emulator/device instance, you can add an entry to /data/local.prop on the device.

Kaceykachina answered 15/1, 2012 at 14:11 Comment(1)
Tried this. It is not working. /dev/null is empty alwaysSecondrate
W
4

Another option:

#include <sstream>

class MyStream
{
private:
   std::stringstream m_ss;
   int m_logLevel;
public:

   MyStream(int Xi_logLevel)
   {
      m_logLevel = Xi_logLevel;
   };
   ~MyStream()
   {
      __android_log_print(m_logLevel,LOG_TAG,"%s", m_ss.str().c_str());
   }

   template<typename T> MyStream& operator<<(T const& Xi_val)
   {
      m_ss << Xi_val;
      return *this;
   }
};

#define MY_LOG(LOG_LEVEL) MyStream(ANDROID_LOG_##LOG_LEVEL) << __FUNCTION__ << ":" << __LINE__ << " : "

PROS:

(1) The messages are printed immediately.

CONS:

(1) You must to change your code (std::cout -> MY_LOG(X)).

(2) Each a single print produces an object and destroys it.

(*** This answer base on this answer)

Wharton answered 4/8, 2016 at 3:56 Comment(0)
F
3

Simple way to print log in android studio logcat.

Step 1: Add the following lines to your common header file.

#include <android/log.h>
    
#define LOGV(TAG, ...) __android_log_print(ANDROID_LOG_VERBOSE, TAG,__VA_ARGS__)
#define LOGD(TAG, ...) __android_log_print(ANDROID_LOG_DEBUG  , TAG,__VA_ARGS__)
#define LOGI(TAG, ...) __android_log_print(ANDROID_LOG_INFO   , TAG,__VA_ARGS__)
#define LOGW(TAG, ...) __android_log_print(ANDROID_LOG_WARN   , TAG,__VA_ARGS__)
#define LOGE(TAG, ...) __android_log_print(ANDROID_LOG_ERROR  , TAG,__VA_ARGS__)

Step 2: Now just call

LOGV("MYTAGNAME", "Hello!!");
LOGD("MYTAGNAME", "Hello!!");
LOGI("MYTAGNAME", "Hello!!");
LOGW("MYTAGNAME", "Hello!!");
LOGE("MYTAGNAME", "Hello!!");

Step 3: you can see logs in logcat as below.

enter image description here

Factorize answered 19/5, 2023 at 7:49 Comment(1)
Perfect simple answer. And it works. Just one thing, if you are using CMake you must also add -llog into target_link_libraries: target_link_libraries(<your_library_name> -llog)Zanze
S
2

The answer of Dietmar Kühl is very good, but it do not work with boost.log from Crystax NDK. I found another idea and have corrected it a little. Here is the code:

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <android/log.h>

static int pfd[2];
static pthread_t thr;
static const char *tag = "myapp";

static void *thread_func(void*)
{
    ssize_t rdsz;
    char buf[128];
    while((rdsz = read(pfd[0], buf, sizeof buf - 1)) > 0) {
        if(buf[rdsz - 1] == '\n') --rdsz;
        buf[rdsz] = 0;  /* add null-terminator */
        __android_log_write(ANDROID_LOG_DEBUG, tag, buf);
    }
    return 0;
}

int start_logger(const char *app_name)
{
    tag = app_name;

    /* make stdout line-buffered and stderr unbuffered */
    setvbuf(stdout, 0, _IOLBF, 0);
    setvbuf(stderr, 0, _IONBF, 0);

    /* create the pipe and redirect stdout and stderr */
    pipe(pfd);
    dup2(pfd[1], 1);
    dup2(pfd[1], 2);

    /* spawn the logging thread */
    if(pthread_create(&thr, 0, thread_func, 0) == -1)
        return -1;
    pthread_detach(thr);
    return 0;
}

And its use:

...
start_logger("MyApp");
...

Now all output from boost.log to std::cout and std::cerr will be in logcat:

#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sources/logger.hpp>

...
boost::log::add_console_log(std::cout);
boost::log::add_common_attributes();

boost::log::sources::logger_mt lg;
BOOST_LOG(lg) << "Hello, World!";
...
Shanley answered 2/8, 2015 at 21:57 Comment(0)
A
0

Based on https://mcmap.net/q/364608/-is-quot-std-cout-quot-usable-in-android-ndk answer in here, but debugged and simplified as following.

  • Managing EOF on overflow function is useless, it would stop logging afterwards.
  • the return condition on overflow function is inverted so it didn't work
  • the buffer copy on sync is useless, it can be done with only one buffer.
  • the setp call on sync can be done with a simple pbump
  • added the option to select different tag and log level for cout and cerr
  • added a class StdStreamRedirector to simplify the assignment to cout and cerr and flush at shutdown.
#ifndef STDSTREAMREDIRECTOR_H
#define STDSTREAMREDIRECTOR_H

#include <android/log.h>
#include <iostream>

class StreamBuf2Log : public std::streambuf {
public:
    enum Type { OUT, ERR };
    StreamBuf2Log(Type t) {
        this->setp(buffer, buffer + bufsize - 2); // extra space for \0 char at the end
        switch (t) {
            case OUT:
                PRIORITY = ANDROID_LOG_INFO;
                strcpy(TAG,"std::cout");
                break;
            case ERR:
                PRIORITY = ANDROID_LOG_ERROR;
                strcpy(TAG, "std::cerr");
                break;
        }
    }

private:
    int overflow(int c)
    {
        *this->pptr() = traits_type::to_char_type(c); // there is just enough space for c and \0 on buffer
        pbump(1);
        sync();
        return 0;
    }

    int sync()
    {
        int n = pptr () - pbase ();
        if (!n || (n == 1 && buffer[0] == '\n')) return 0; // spurious flushes removed
        buffer[n] = '\0';
        __android_log_write(PRIORITY, TAG, buffer);
        pbump (-n);  // Reset pptr().
        return 0;
    }

    static constexpr int bufsize = 2048;
    char TAG[10];
    android_LogPriority PRIORITY;
    char buffer[bufsize];
};

class StdStreamRedirector {
private:
    StreamBuf2Log outbuf;
    StreamBuf2Log errbuf;
public:
    StdStreamRedirector() : outbuf(StreamBuf2Log::OUT), errbuf(StreamBuf2Log::ERR) {
        std::cout.rdbuf(&outbuf);
        std::cerr.rdbuf(&errbuf);
    }
    ~StdStreamRedirector() {
        std::cout << std::flush;
        std::cerr << std::flush;
    }
};

#endif //STDSTREAMREDIRECTOR_H

To start the redirection at the loading of the so library and stopping when the so library use the following:

#include "StdStreamRedirector.h"

StdStreamRedirector *redirector;

jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
    redirector = new StdStreamRedirector();
    return JNI_VERSION_1_6;
}

void JNI_OnUnLoad(JavaVM* vm, void* /*reserved*/) {
    delete redirector;
}
Astrometry answered 13/1 at 0:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.