はい、NDK logcat はそれについて馬鹿げています。stderr/stdout を logcat にリダイレクトする方法はありますが、欠点があります (ルート化されたデバイス専用の「adb shell setprop」が必要か、dup() のような手法が必要ですが、その目的のためだけにスレッドを作成するのは適切ではありません)。組み込みデバイスに関するアイデアの私見ですが、この手法については以下でさらに調べることができます)。
そのため、その目的のために独自の関数/マクロを作成しました。ここにスニペットがあります。debug.c で、次のようにします。
#include "debug.h"
#include <stdio.h>
#include <stdarg.h>
static const char LOG_TAG[] = "jni";
void android_log(android_LogPriority type, const char *fmt, ...)
{
static char buf[1024];
static char *bp = buf;
va_list vl;
va_start(vl, fmt);
int available = sizeof(buf) - (bp - buf);
int nout = vsnprintf(bp, available, fmt, vl);
if (nout >= available) {
__android_log_write(type, LOG_TAG, buf);
__android_log_write(ANDROID_LOG_WARN, LOG_TAG, "previous log line has been truncated!");
bp = buf;
} else {
char *lastCR = strrchr(bp, '\n');
bp += nout;
if (lastCR) {
*lastCR = '\0';
__android_log_write(type, LOG_TAG, buf);
char *rest = lastCR+1;
int len = bp - rest; // strlen(rest)
memmove(buf, rest, len+1); // no strcpy (may overlap)
bp = buf + len;
}
}
va_end(vl);
}
次に、debug.h で次のようにします。
#include <android/log.h>
void android_log(android_LogPriority type, const char *fmt, ...);
#define LOGI(...) android_log(ANDROID_LOG_INFO, __VA_ARGS__)
#define LOGW(...) android_log(ANDROID_LOG_WARN, __VA_ARGS__)
...
ここで、debug.hpp をインクルードし、'\n' が検出される (またはバッファーがいっぱいになる) までバッファーされた printf のようなセマンティックで LOGI() を呼び出すだけです。
ただし、呼び出しから生成された文字列がバッファよりも長い場合、切り捨てられて出力されるため、これは完全ではありません。しかし、率直に言って、ほとんどの場合、1024 文字で十分です (これよりも少ない場合もあります)。とにかく、これが発生した場合、警告が出力されるので、それについて知ることができます。
また、vsnprintf() は標準 C ではないことに注意してください (ただし、Android NDK で動作します)。代わりに vsprintf() を使用することもできますが (これは標準です)、それ自体は安全ではありません。
================================================== ====================
dup() 手法については、こちらを参照してください( James Mooreの回答)。
次に、上記の関数を取り除き、マクロを次のように定義できます。
#define LOG(...) fprintf(stderr, ...)
これで完了です。
利点:
- C/C++ ライブラリは、多くの場合、ログに stderr を使用します。dup を使用することは、コードを変更せずに logcat に出力する唯一の方法です (一部の大きなものは、fprintf(stderr, ...) への何百もの直接呼び出しを使用します)。
- stderr は、何十年も使用されている標準 C です。ストリームに関連するすべての標準 C ライブラリ関数を使用できます。C++ の場合も同様です。cerr を << 演算子と共に使用することもできます。内部では動作しますが、まだ stderr です。
- 非常に長い行は切り捨てられません (代わりに、分割されます)。より短いバッファーを使用する正当な理由 (例では 256)。
短所:
- 単独のスレッド (IO のみのスレッドですが、影響はほとんどありません)
- 通話中にログの優先度の値 (INFO、WARN、ERROR など) を選択することはできません。デフォルトのもの (INFO) を使用するため、DMMS は常に stderr 行を同じ色で表示します。