Является ли "std:: cout" пригодным для использования в Android-ndk
В Android-ndk мы можем использовать "__android_log_write", "__android_log_print",... и т.д. для вывода сообщений в окно "LogCat". Как насчет того, если я использую "std:: cout" для вывода некоторых строк? Например.
std::cout << "some strings" << std::endl;
Где будут отправлены строки.
Кажется, что Android не имеет консольных приложений, и указанные выше строки не могут быть отправлены. Могу ли я перенаправить "stdout" в файл, чтобы отправка строк в "std:: cout" эквивалентна протоколированию сообщений?
Ответы
Ответ 1
Согласно документации Android, stdout и stderr выводятся в /dev/null
. Вы можете использовать Android Debug Bridge, чтобы достичь желаемого.
По умолчанию система Android отправляет выходы stdout и stderr (System.out и System.err) в /dev/null. В процессах, которые запускают Dalvik VM, вы можете написать системе копию копии файла журнала. В этом случае система записывает сообщения в журнал, используя теги журнала stdout и stderr, как с приоритетом I. Чтобы маршрутизировать вывод таким образом, вы останавливаете запуск экземпляра эмулятора/устройства, а затем используете команду shell setprop, чтобы включить перенаправление вывода. Вот как вы это делаете:
$ adb shell stop
$ adb shell setprop log.redirect-stdio true
$ adb shell start
Система сохраняет этот параметр до тех пор, пока вы не закончите экземпляр эмулятора/устройства. Чтобы использовать параметр по умолчанию в экземпляре эмулятора/устройства, вы можете добавить запись на /data/local.prop на устройстве.
Ответ 2
Вы можете создать класс, полученный из std::streambuf
, который использует специальные функции Android для отправки созданной последовательности символов. Однако я не знаю, где, по умолчанию, реализация std::cout
отправляет символы на Android. В принципе, это будет выглядеть примерно так:
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];
};
Чтобы на самом деле настроить std::cout
для записи в этот буфер потока, вы бы сделали что-то подобное в своей функции main()
:
int main() {
std::cout.rdbuf(new androidbuf);
...
}
Это создает утечку памяти для одного потока androidbuf
, который, однако, несколько преднамерен: поток может быть записан после того, как main()
завершен, и он очищается, когда std::cout
уничтожается. Если вы этого не хотите, вы можете восстановить исходный буфер потока std::cout
или установить его в null и удалить возврат из rdbuf()
:
// avoid a one-time resource leak but don't get output afterwards:
delete std::cout.rdbuf(0);
Ответ 3
Другая опция:
#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__ << " : "
ПЛЮСЫ:
(1) Сообщения печатаются немедленно.
МИНУСЫ:
(1) Вы должны изменить свой код (std:: cout → MY_LOG (X)).
(2) Каждая отдельная печать создает объект и уничтожает его.
(***
Эта база ответов на этот ответ)
Ответ 4
Ответ Dietmar Kühl очень хороший, но он не работает с boost.log из Crystax NDK. Я нашел другую идею и немного ее исправил. Вот код:
#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;
}
И его использование:
...
start_logger("MyApp");
...
Теперь все выходные данные из boost.log в std:: cout и std:: cerr будут в 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!";
...