[英]Get all calls to specific function in source file and generate other files (using C, C++ preprocessor or scripts)
I have a program like below: 我有一个程序如下:
// test.c
#include <stdio.h>
#define LOG printf
int main(int argc, char *argv[])
{
LOG("Hello, my name %s, my age %d\n", "John", 30);
LOG("I have wife and %d kids\n", 2);
return 0;
}
I want to create a log file which contains all texts like: 我想创建一个包含所有文本的日志文件,例如:
// message.txt
LINE: "Hello, my name %s, my age %d\n"
LINE: "I have wife and %d kids\n"
Is it possible to do it using C's preprocessor , make , or even a script ? 是否可以使用C的预处理器 , make甚至脚本来完成 ?
Edited: 编辑:
Let's say I have a program that prints those messages in English already, I have source code for sure, now I want to support French then: 假设我有一个程序已经可以用英语打印这些消息了,我肯定有源代码,现在我要支持法语:
I need to get all messages that developers put into their program and create a message.txt
which contains all texts, replace each message in original program with an identify number which maps each message in message.txt
. 我需要获取开发人员放入其程序中的所有消息,并创建一个包含所有文本的
message.txt
,将原始程序中的每条消息替换为一个标识号,该标识号将message.txt
每条消息映射。 I want to do it programmatically not manually. 我想以编程方式而不是手动进行。
What you are looking for is localization, read something about it here . 您正在寻找的是本地化,请在此处阅读有关它的内容。 If you're on a posix system you can use
gettext()
for that, see manpage . 如果您使用的是posix系统,则可以为此使用
gettext()
,请参阅manpage 。 If you're using Windows you can take a look at GetText for Windows . 如果您使用的是Windows,则可以查看Windows的GetText 。
Nevertheless I wrote a little C++ program that generate the files you want, see code below. 尽管如此,我还是写了一个小的C ++程序来生成所需的文件,请参见下面的代码。 You call it via
myProg filename logFunction
where filename
is the source file you want to input and logFunction
is for example LOG
. 您可以通过
myProg filename logFunction
调用它,其中filename
是要输入的源文件,而logFunction
例如是LOG
。
You will get a map file called filename_map
and a new source file filename_new
. 您将获得一个名为
filename_map
的映射文件和一个新的源文件filename_new
。 The new source file will contain a new macro LOG
and a function readLocaleStrings()
which will read all the strings at program start from the filename_map
file. 新的源文件将包含一个新的宏
LOG
和一个函数readLocaleStrings()
,该函数将在程序开始时从filename_map
文件读取所有字符串。 The new macro LOG
will use the right strings automatically then. 然后,新的宏
LOG
将自动使用正确的字符串。
In your example you would call it by myProg test.c LOG
and would get: 在您的示例中,将通过
myProg test.c LOG
对其进行调用,并将获得:
test_map.txt
: test_map.txt
:
7: "Hello, my name %s, my age %d\n"
8: "I have wife and %d kids\n"
and a test_new.c
: 和一个
test_new.c
:
// test.c
#include <stdio.h>
#define LOG(filename,line,...) printf(localeStrings.strings[localeStrings.lastIdx++], __VA_ARGS__)
/* Beginning: Generated code for localization */
#include <stdlib.h>
#include <string.h>
typedef struct {
size_t count;
size_t lastIdx;
char ** strings;
} stringArray_t;
stringArray_t localeStrings = { 0, 0, NULL };
int readLocaleStrings (const char * const filename)
{
FILE * file = NULL;
char * line = NULL;
char * str = NULL;
size_t len = 0;
ssize_t read;
file = fopen(filename, "r");
if (file == NULL)
{
return -1;
}
localeStrings.strings = malloc (sizeof (char *));
localeStrings.count = 0;
while (-1 != (read = getline(&line, &len, file)))
{
size_t curIdx = localeStrings.count++;
localeStrings.strings = realloc(localeStrings.strings, localeStrings.count * sizeof (char *));
str = strstr(line, "\"");
localeStrings.strings[curIdx] = malloc(sizeof (char) * (size_t)(1 + read));
strcpy (localeStrings.strings[curIdx], str);
}
fclose(file);
if (line)
{
free(line);
}
return 0;
}
void freeLocaleStrings()
{
size_t idx;
for (idx = 0; idx < localeStrings.count; ++idx)
{
free(localeStrings.strings[idx]);
}
free(localeStrings.strings);
}
/* End: Generated code for localization */
int main(int argc, char *argv[])
{
readLocaleStrings("test_map.txt");
LOG("test_map.txt", "7", "John", 30);
LOG("test_map.txt", "8", 2);
freeLocaleStrings();
return 0;
}
C++ program: C ++程序:
The C++ program has limitation, it will just find LOG("
if you want also to find LOG
with spaces anywhere like: LOG ("
you have to change that code. Moreover there are cases such as multiple lines messages, messages that had been commented out etc. You have to extend the code for your requirements then. Also there is no great argument parse handling or error checking. Further for the writing of the helper code it is needed to find the main function. But for your given input file it works perfectly and should just point you in the right direction. To extend it more easy and make it more flexible using a regex library would be wise. 在C ++程序有局限性,它只会找到
LOG("
如果你想也找到LOG
与空间的任何地方,如: LOG ("
你必须更改代码而且有这样的情况,例如多行的消息,已被评论的邮件然后必须根据需要扩展代码,也没有很好的参数解析处理或错误检查方法,此外,编写辅助代码时需要找到主函数,但对于给定的输入文件,可以完美地工作,并且应该为您指明正确的方向。使用正则表达式库使其更容易扩展并使其更灵活将是明智的。
Code: 码:
#include <iostream>
#include <fstream>
/* Function definition is under main function */
const char * getNextHelperFunctionLine();
std::string getExtension (const std::string filename)
{
/* get dot of e.g. foo.c */
size_t posDot = filename.rfind('.');
/* extract extension */
std::string extension;
if (std::string::npos != posDot)
{
/* extension found */
extension = filename.substr(posDot);
}
return extension;
}
std::string getFilename (const std::string filename)
{
/* get dot of e.g. foo.c */
size_t posDot = filename.rfind('.');
/* extract filename */
std::string name = filename;
if (std::string::npos != posDot)
{
name = name.substr(0, posDot);
}
return name;
}
int main (int argc, char* argv[])
{
if (argc < 3)
{
std::cerr << "Usage: "
<< " " << argv[0] << " filename logFunction"
<< std::endl;
return 0;
}
std::string infileName (argv[1]);
/* extract filename and extension */
std::string filename = getFilename(infileName);
std::string extension = getExtension(infileName);;
/* names for generated files */
std::string mapfileName = filename + "_map.txt";
std::string mappedfileName = filename + "_new" + extension;
/* open streams for input and output */
std::ifstream infile(infileName.c_str());
std::ofstream fileMap(mapfileName.c_str());
std::ofstream fileMapped(mappedfileName.c_str());
/* string for log function e.g. "LOG(" */
std::string logFun = std::string(argv[2]);
std::string logFunOpen = logFun + "(\"";
std::string lineRead;
size_t lineNr = 1;
size_t mainParanthesis = 0;
bool mainReturnFound = false;
/* Loop through whole input file */
while (std::getline(infile, lineRead))
{
/* position of log function opening e.g. "LOG(" */
size_t posLogOpen = lineRead.find(logFunOpen);
/* opening found? */
bool foundOpen = std::string::npos != posLogOpen;
if (foundOpen)
{
bool foundClose = false;
/* position of the string beginning */
size_t posLogStringBeg = posLogOpen + logFunOpen.length();
size_t posLogClose = posLogStringBeg;
/* find closing of the log function e.g. "LOG(...)" */
while (!foundClose)
{
/* search for '"' and skip these if they are in the string */
posLogClose = lineRead.find("\"", posLogClose + 1);
if (std::string::npos != posLogClose)
{
foundClose = (0 != lineRead.compare(posLogClose - 1, 1, "\\"));
}
}
/* closing found write map file and new source file */
if (foundClose)
{
size_t len = posLogClose - posLogStringBeg;
fileMap << lineNr << ": \""
<< lineRead.substr(posLogStringBeg, len) << "\""
<< std::endl;
fileMapped << lineRead.substr(0, posLogStringBeg - 1)
<< '"' << mapfileName << "\", "
<< '"' << lineNr
<< lineRead.substr(posLogClose)
<< std::endl;
}
}
/* not a log function write normal code */
else
{
if ( std::string::npos != lineRead.find("#define")
&& std::string::npos != lineRead.find(logFun))
{
/* log functions needs to be changed */
fileMapped << "#define "
<< logFun << "(filename,line,...) "
<< "printf(localeStrings.strings[localeStrings.lastIdx++], __VA_ARGS__)" << std::endl;
}
else if ( 0 == mainParanthesis
&& std::string::npos != lineRead.find(" main")
&& std::string::npos != lineRead.find("(")
&& std::string::npos != lineRead.find(")"))
{
/* found main function write all helper functions in front of it */
const char * helperLine;
while ((helperLine = getNextHelperFunctionLine()))
{
fileMapped << helperLine << std::endl;
}
/* write main function part */
fileMapped << lineRead << std::endl;
/* is there an opening parenthesis? */
if (std::string::npos != lineRead.find("{"))
{
++mainParanthesis;
fileMapped << " readLocaleStrings(\"" << mapfileName << "\");" << std::endl;
}
}
else
{
/* in main function write first part */
if (std::string::npos != lineRead.find("{"))
{
/* write opening */
fileMapped << lineRead << std::endl;
if (0 == mainParanthesis)
{
fileMapped << " readLocaleStrings(\"" << mapfileName << "\");" << std::endl;
}
++mainParanthesis;
}
/* return statement? */
else if ( 1 == mainParanthesis
&& std::string::npos != lineRead.find("return"))
{
mainReturnFound = true;
fileMapped << " freeLocaleStrings();" << std::endl;
/* write return */
fileMapped << lineRead << std::endl;
}
else if ( 1 == mainParanthesis
&& std::string::npos != lineRead.find("}"))
{
--mainParanthesis;
if (!mainReturnFound)
{
fileMapped << " freeLocaleStrings();" << std::endl;
}
/* write closing */
fileMapped << lineRead << std::endl;
}
else
{
/* write other code */
fileMapped << lineRead << std::endl;
}
}
}
++lineNr;
}
return 0;
}
const char * getNextHelperFunctionLine()
{
static size_t idx = 0;
static const char * helperFunLines[] =
{
"",
"/* Beginning: Generated code for localization */",
"#include <stdlib.h>",
"#include <string.h>",
"",
"typedef struct {",
" size_t count;",
" size_t lastIdx;",
" char ** strings;",
"} stringArray_t;",
"",
"stringArray_t localeStrings = { 0, 0, NULL };",
"",
"int readLocaleStrings (const char * const filename)",
"{",
" FILE * file = NULL;",
" char * line = NULL;",
" char * str = NULL;",
" size_t len = 0;",
" ssize_t read;",
"",
" file = fopen(filename, \"r\");",
" if (file == NULL)",
" {",
" return -1;",
" }",
"",
" localeStrings.strings = malloc (sizeof (char *));",
" localeStrings.count = 0;",
"",
" while (-1 != (read = getline(&line, &len, file)))",
" {",
" size_t curIdx = localeStrings.count++;",
" localeStrings.strings = realloc(localeStrings.strings, localeStrings.count * sizeof (char *));",
"",
" str = strstr(line, \"\\\"\");",
" localeStrings.strings[curIdx] = malloc(sizeof (char) * (size_t)(1 + read));",
" strcpy (localeStrings.strings[curIdx], str);",
" }",
"",
" fclose(file);",
" if (line)",
" {",
" free(line);",
" }",
"",
" return 0;",
"}",
"",
"void freeLocaleStrings()",
"{",
" size_t idx;",
" for (idx = 0; idx < localeStrings.count; ++idx)",
" {",
" free(localeStrings.strings[idx]);",
" }",
" free(localeStrings.strings);",
"}",
"/* End: Generated code for localization */",
""
};
if (idx < (sizeof (helperFunLines) / sizeof (helperFunLines[0])))
{
return helperFunLines[idx++];
}
else
{
return nullptr; /* use NULL if compiler doesn't support nullptr */
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.