Full name of operators in LLVM - llvm

How do I get the whole name of an operator in LLVM?
I'm iterating through blocks, and then, in each of their instructions, I try to get the operator name, but I do only get a part of it. Running the following code:
virtual bool runOnBasicBlock(BasicBlock &bb) {
for (auto it(bb.begin()); it != bb.end(); ++it) {
errs() << it->getName() << '\t' << *it << '\n';
}
}
I get output lines like:
icmp %cmp = icmp slt i32 %i.0, %argc
icmp %cmp1 = icmp sgt i32 %call, %max.0
add %inc = add nsw i32 %i.0, 1
I'd like to get icmp slt, icmp sgt, and add nsw, instead of icmp and add.

Well, slt, sgt and others for icmp are just arguments. You can access them with getPredicate (a method of CmpInst). Also see the useful function getPredicateText in lib/IR/AsmWriter.cpp.
For stuff like nsw, check out the method hasNoSignedWrap and similar methods.

Related

How do I get the value that is written by a store instruction in Pin?

I am currently using Pin and I want to get the value that a store instruction is writing. The problem that I am facing is that even though I can insert a callback before the write instruction (using IPOINT_BEFORE) and get a value from the memory address that will be written, it obviously isn't the correct one since the writing hasn't happened yet. I cannot use IARG_MEMORYWRITE_EA and IPOINT_AFTER as arguments together.
I have managed to make it work when there is a load instruction, since the value is already in memory. The code for that is below.
void Read(THREADID tid, ADDRINT addr, ADDRINT inst){
PIN_GetLock(&globalLock, 1);
ADDRINT * addr_ptr = (ADDRINT*)addr;
ADDRINT value;
PIN_SafeCopy(&value, addr_ptr, sizeof(ADDRINT));
fprintf(stderr,"Read: ADDR, VAL: %lx, %lu\n", addr, value);
.
.
.
PIN_ReleaseLock(&globalLock);
}
VOID instrumentTrace(TRACE trace, VOID *v)
{
for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) {
for (INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins = INS_Next(ins)) {
if(INS_IsMemoryRead(ins)) {
INS_InsertCall(ins,
IPOINT_BEFORE,
(AFUNPTR)Read,
IARG_THREAD_ID,
IARG_MEMORYREAD_EA,
IARG_INST_PTR,
IARG_END);
} else if(INS_IsMemoryWrite(ins)) {
INS_InsertCall(ins,
IPOINT_BEFORE,
(AFUNPTR)Write,
IARG_THREAD_ID,//thread id
IARG_MEMORYWRITE_EA,//address being accessed
IARG_INST_PTR,//instruction address of write
IARG_END);
}
}
}
}
How can I grab the value that a store instruction writes to memory?
I think I managed to do what I was trying to. The way I get the values is that every time there is a store in the program, I save the memory address that it will write to. Then I instrument every single instruction and call the WriteData function, which essentially gets the data from the memory address that i previously saved, just like with Reads.
This is the code for getting the value of a load instruction.
void Read(THREADID tid, ADDRINT addr, ADDRINT inst){
PIN_GetLock(&globalLock, 1);
ADDRINT * addr_ptr = (ADDRINT*)addr;
ADDRINT value;
PIN_SafeCopy(&value, addr_ptr, sizeof(ADDRINT));
fprintf(stderr,"Read: ADDR, VAL: %lx, %lx\n", addr, value);
...
PIN_ReleaseLock(&globalLock);
}
This is the code for grabbing the address of store instruction.
void Write(THREADID tid, ADDRINT addr, ADDRINT inst ){
PIN_GetLock(&globalLock, 1);
writeaddr = addr;
writecount++;
...
PIN_ReleaseLock(&globalLock);
}
This is the code for getting the data from the address of the previous store.
void WriteData(){
PIN_GetLock(&globalLock, 1);
//Reading from memory
if (writecount > 0){
ADDRINT * addr_ptr = (ADDRINT*)writeaddr;
ADDRINT value;
PIN_SafeCopy(&value, addr_ptr, sizeof(ADDRINT));
fprintf(stderr,"Write: ADDR, Value: %lx, %lx\n", writeaddr, value);
writecount--;
}
PIN_ReleaseLock(&globalLock);
}
But a minor problem remains. The following is the data from the microbenchmark that I use and after that are the printouts in the terminal.
for (i = 0; i < MAX; i++) {
a[i] = i;
}
for (i = 0; i < MAX; i++) {
a[i] = a[i] + 1;
b[i] = a[i];
}
MAX is 5.
Write: ADDR, Value: 601078, 6f
Read: ADDR, VAL: 7ffd0560de10, 40051b
Write: ADDR, Value: 601080, 0
Write: ADDR, Value: 601084, 1
Write: ADDR, Value: 601088, 2
Write: ADDR, Value: 60108c, 3
Write: ADDR, Value: 601090, 4
Read: ADDR, VAL: 601080, 100000000
Write: ADDR, Value: 601080, 100000001
Write: ADDR, Value: 601060, 1
Read: ADDR, VAL: 601084, 200000001
Write: ADDR, Value: 601084, 200000002
Write: ADDR, Value: 601064, 2
Read: ADDR, VAL: 601088, 300000002
Write: ADDR, Value: 601088, 300000003
Write: ADDR, Value: 601068, 3
Read: ADDR, VAL: 60108c, 400000003
Write: ADDR, Value: 60108c, 400000004
Write: ADDR, Value: 60106c, 4
Read: ADDR, VAL: 601090, 4
Write: ADDR, Value: 601090, 5
Write: ADDR, Value: 601070, 5
From what we see in the terminal, it seems that the first writes to a[i], happen as expected. But then, when the program is reading the same addresses instead of getting 1,2,etc, it gets 100000001 and so on. It correctly increments them by 1. But when the time comes to store them to b[i], the values are again correct. So I am wondering why I encounter this behaviour with the data I get from reads.

Faster processing of SendARP function

Originally posted here, but found off topic: https://serverfault.com/questions/617459/faster-processing-of-sendarp-function
I've been working on a network scanner for Windows. I have successfully written the code, but the problem is it takes too much time to scan the hosts that aren't up. When I tried scanning a subnet (1 to 255), it took more than half hour. I couldn't find a function to control the time limit or a way to control the time-out of SendARP function.
DestIp = inet_addr(strn.c_str()); //Setting Destination IPv4 dotted-decimal address into a proper address for the IN_ADDR structure.
SrcIp = inet_addr(SrcIpString);
memset(&MacAddr, 0xff, sizeof(MacAddr)); //Initializing MAC Address to ff-ff-ff-ff-ff-ff
dwRetVal = SendARP(DestIp, SrcIp, &MacAddr, &PhysAddrLen); //Sending ARP request to the destined IP Address
if (dwRetVal == NO_ERROR) {
bPhysAddr = (BYTE *)& MacAddr;
if (PhysAddrLen) {
std::cout << strn<<std::endl;
for (int i = 0; i < (int)PhysAddrLen; i++) {
if (i == ((int)PhysAddrLen - 1))
printf("%.2X\n", (int)bPhysAddr[i]);
else
printf("%.2X-", (int)bPhysAddr[i]);
}
}
}
You're using a convenience function from the "IP Helper" library. That's not performance-oriented.
The ServerFault comments actually hit the mail on the head: use threads. With <thread> that's nowadays quite simple. Just do 255 std::async calls to your function. Of course, make sure that all the MacAddr and PhysAddrLen references aren't invalidated.

TCP Recv using select() returning 1 byte reads

In my code below, I am doing a send(23 bytes) and then in an infinite while loop doing a recv whenever data is ready(as per the return from the select call). The problem is that I always seem to end up getting 1 byte reads from the recv(). I understand that the TCP is stream oriented, and there is no guarantee of what size I'll get back, but if I enable the code I've disabled below(i.e. do a recv in a while loop infitiely) - I always seem to read 29 bytes(what I expect). However, if I use the select function call, recv only reads 1 byte in every iteration. It keeps doing this forever(likely when its finished reading all the data).
int rc = 0, numBytes=0 ;
char reply[256] ;
// Send a ping
char PingPacket[23] ;
PreparePingPacket(PingPacket) ;
numBytes = send(m_sockfd,PingPacket,23,0);
/*----- COMMENTED OUT
while(1){
rc = recv(m_sockfd, reply, 256, 0) ;
if(rc == -1)
{
fprintf(stderr, "Error on Sending Ping .. \n") ;
return ;
}else{
ParsePacket(reply, rc) ;
}
}
*/ END OF COMMENTED PART
// Now wait for all types of pongs to come back
fd_set rfs ;
while(1)
{
memset(reply,'\0',256) ;
FD_ZERO(&rfs) ;
FD_SET(m_sockfd,&rfs) ;
// Wait for ever ..
if((rc = select(m_sockfd+1, &rfs, NULL, NULL, NULL)) < 0)
{
fprintf(stderr, "Select Error after connection .. \n") ;
close(m_sockfd) ;
return ;
}
if(FD_ISSET(m_sockfd, &rfs))
{
cout << "Read. for reading" << endl ;
int rc = 0 ;
if((rc = recv(m_sockfd, reply, sizeof(reply), 0)) == -1)
{
cout << "Could not read anything .... " << endl ;
fprintf(stderr,"Error: %d\n",errno) ;
return ;
}
else if(rc == 0)
{
cout << "The other side closed the connection. \n" << endl ;
}
}
.....
Any ideas on why this is happening ?
in the commented out section you have
rc = recv(m_sockfd, reply, 256, 0) ;
and in the select you have
if((rc = recv(m_sockfd, reply, sizeof(reply), 0)) == -1)
no sure if the definition of replay is the same function as the recv or you passed it as a parameter. Try to print the sizeof(replay) before the call to see that is actually 256.
Once select() informs you about data available, you have to call accept() and then determine a new socket to actually read that data.
So, you can think about select() as an "ear socket". Once the "ear" hears something, it creates another socket dedicated to send-receive TCP stream between you and "heared" remote party. Once remote party is no longer available, you have to close that socket. Same time, "ear" socket persists until you're interested in serving incoming connection requests.

Qt Serial Port - Reading data consistently

I am sending (writing) bytes to a device via my serial port. I am using the QSerialPort (http://qt-project.org/wiki/QtSerialPort) module to instantiate device IO support. When I send messages to my INSTEON modem (serial), upon reading my message the device sends back a copy of my message + 0x06 (ACK Byte) followed by a status message.
I have tested my message using DockLight (http://www.docklight.de/). I send the following message to query the state of the device:
02 62 1D E9 4B 05 19 00
Using Docklight, I receive the response:
02 62 1D E9 4B 05 19 00 06 02 50 20 CB CF 1E DA F7 21 00 FF
The returned message indicates exactly what I would expect, that the device is on. If off, the modem would send back 0x00 in the last byte position if the device was off. Now, my problem - I must not have my function setup properly to send and then receive the response bytes. I have tried many different examples and configurations, currently I am using the following:
Setup signal-slot connections:
QObject::connect(&thread, SIGNAL(sendResponse(QByteArray)),
this, SLOT(handleResponse(QByteArray)));
QObject::connect(&thread, SIGNAL(error(QString)),
this, SLOT(processError(QString)));
QObject::connect(&thread, SIGNAL(timeout(QString)),
this, SLOT(processTimeout(QString)));
Function used to iterate through QList of devices. If device is desired type ("Light"), then we format the device ID to the intended QByteArray message structure. Pass message to thread for sending. (Thread modified from QSerialPort BlockingMaster example.
void Device::currentStatus(QList<Device *> * deviceList){
QString devID, updateQry;
int devStatus, updateStatus;
updateStatus=0;
QSqlQuery query;
for(int i=0; i<deviceList->size(); i++){
if(deviceList->at(i)->type == "Light"){
devStatus = deviceList->at(i)->status;
devID = deviceList->at(i)->deviceID;
QByteArray msg;
bool msgStatus;
msg.resize(8);
msg[0] = 0x02;
msg[1] = 0x62;
msg[2] = 0x00;
msg[3] = 0x00;
msg[4] = 0x00;
msg[5] = 0x05;
msg[6] = 0x19;
msg[7] = 0x00;
msg.replace(2, 3, QByteArray::fromHex( devID.toLocal8Bit() ) );
qDebug() << "Has device " << deviceList->at(i)->name << "Changed?";
//send(msg,&msgStatus, &updateStatus);
//msg.clear();
thread.setupPort("COM3",500,msg);
if(devStatus!=updateStatus){
qDebug() << deviceList->at(i)->name << " is now: " << updateStatus;
updateStatus = !updateStatus;
}
}
}
}
SetupThread function used to set local thread variables and executes (runs) thread.
void serialThread::setupPort(const QString &portName, int waitTimeout, const QByteArray &msg){
qDebug() << "Send Message " << msg.toHex();
QMutexLocker locker(&mutex);
this->portName = portName;
this->waitTimeout = waitTimeout;
this->msg = msg;
if(!isRunning())
start();
else
cond.wakeOne();
}
Run Function - Handled sending and receiving
void serialThread::run(){
bool currentPortNameChanged = false;
qDebug() << "Thread executed";
mutex.lock();
QString currentPortName;
if(currentPortName != portName){
currentPortName = portName;
currentPortNameChanged = true;
}
int currentWaitTimeout = waitTimeout;
QByteArray sendMsg = msg;
mutex.unlock();
QSerialPort serial;
while(!quit){
if(currentPortNameChanged){
serial.close();
serial.setPortName("COM3");
if (!serial.open(QIODevice::ReadWrite)) {
emit error(tr("Can't open %1, error code %2")
.arg(portName).arg(serial.error()));
return;
}
if (!serial.setBaudRate(QSerialPort::Baud19200)) {
emit error(tr("Can't set baud rate 9600 baud to port %1, error code %2")
.arg(portName).arg(serial.error()));
return;
}
if (!serial.setDataBits(QSerialPort::Data8)) {
emit error(tr("Can't set 8 data bits to port %1, error code %2")
.arg(portName).arg(serial.error()));
return;
}
if (!serial.setParity(QSerialPort::NoParity)) {
emit error(tr("Can't set no patity to port %1, error code %2")
.arg(portName).arg(serial.error()));
return;
}
if (!serial.setStopBits(QSerialPort::OneStop)) {
emit error(tr("Can't set 1 stop bit to port %1, error code %2")
.arg(portName).arg(serial.error()));
return;
}
if (!serial.setFlowControl(QSerialPort::NoFlowControl)) {
emit error(tr("Can't set no flow control to port %1, error code %2")
.arg(portName).arg(serial.error()));
return;
}
}
//write request
serial.write(msg);
if (serial.waitForBytesWritten(waitTimeout)) {
//! [8] //! [10]
// read response
if (serial.waitForReadyRead(currentWaitTimeout)) {
QByteArray responseData = serial.readAll();
while (serial.waitForReadyRead(10)){
responseData += serial.readAll();
}
QByteArray response = responseData;
//! [12]
emit this->sendResponse(response);
//! [10] //! [11] //! [12]
} else {
emit this->timeout(tr("Wait read response timeout %1")
.arg(QTime::currentTime().toString()));
}
//! [9] //! [11]
} else {
emit timeout(tr("Wait write request timeout %1")
.arg(QTime::currentTime().toString()));
}
mutex.lock();
cond.wait(&mutex);
if (currentPortName != portName) {
currentPortName = portName;
currentPortNameChanged = true;
} else {
currentPortNameChanged = false;
}
currentWaitTimeout = waitTimeout;
sendMsg = msg;
mutex.unlock();
}
serial.close();
}
handleResponse function, SLOT which receives response signal
void Device::handleResponse(const QByteArray &msg){
qDebug() << "Read: " << msg.toHex();
}
I receive the following output:
Has device "Living Room Light" Changed?
Send Message "02621de94b051900"
Has device "Bedroom Light" Changed?
Send Message "026220cbcf051900"
Thread executed
Read: "026220cbcf05190006"
Polling for changes...
Has device "Living Room Light" Changed?
Send Message "02621de94b051900"
Has device "Bedroom Light" Changed?
Send Message "026220cbcf051900"
Read: "025020cbcf1edaf721000002621de94b05190006"
Polling for changes...
Has device "Living Room Light" Changed?
Send Message "02621de94b051900"
Has device "Bedroom Light" Changed?
Send Message "026220cbcf051900"
Read: "02501de94b1edaf72100ff02621de94b05190006"
Two issues here.
I never receive any response regarding the second device (Bedroom Light), this is the message that is sent second. It seems that the send is being blocked, how would you recommend I format my sending so that I send after the response is received for the first send? There is only 1 COM port that can be used to send/receive. I believe I should Send message to Device 1, receive Device 1 response, Send to Device 2, receive Device 2. Could I end up seeing a huge traffic jam with a lot of devices and using wait conditions, ie. wait for device 1 communication process to finish before executing comm process for device 2?
The very first read contains the appropriate 1st half of the receive. Read: "026220cbcf05190006" The second receive contains the 2nd half of the 1st response followed by the 1st half of the second response: Read 2 - Read: "025020cbcf1edaf721000002621de94b05190006" The appropriate full response is 02621DE94B05190006 025020CBCF1EDAF72100FF
(note 20CBCF is Device 2's ID in the full response example)
What corrections should be made to the way I am receiving data from the serial port?
Thank you!
I believe my problems have shifted from the scope of this question. With help from Kuzulis I have implemented Write/Read functions to successfully send and read serial messages consistently. Kuzulis recommended using the Synchronous blocking communication pattern, however it was later decided that the Asynchronous Non-Blocking method would be best fit for my application.
My implementation closely follows the "Master" example provided with the QSerialPort source files.
I use CurrentStatus to iterate through a QList of Device objects. For each Light in the Device list, I format an 8 Byte message to query the current status of the device (ON/OFF).
void Device::currentStatus(QList<Device *> * deviceList){
QString devID, updateQry;
int devStatus, updateStatus;
updateStatus=0;
QSqlQuery query;
for(int i=0; i<deviceList->size(); i++){
if(deviceList->at(i)->type == "Light"){
devStatus = deviceList->at(i)->status;
devID = deviceList->at(i)->deviceID;
QByteArray msg;
msg.resize(8);
msg[0] = 0x02;
msg[1] = 0x62;
msg[2] = 0x00;
msg[3] = 0x00;
msg[4] = 0x00;
msg[5] = 0x05;
msg[6] = 0x19;
msg[7] = 0x00;
msg.replace(2, 3, QByteArray::fromHex( devID.toLocal8Bit() ) );
qDebug() << "Has device " << deviceList->at(i)->name << "Changed?";
emit writeRequest(msg);
if(devStatus!=updateStatus){
qDebug() << deviceList->at(i)->name << " is now: " << updateStatus;
updateStatus = !updateStatus;
}
}
}
}
In the Device class constructor, I connect the signals and slots:
Device::Device(){
serialTimer.setSingleShot(true);
QObject::connect(&serial, SIGNAL(readyRead()),
this, SLOT(handleResponse()));
QObject::connect(&serialTimer, SIGNAL(timeout()),
this, SLOT(processTimeout()));
QObject::connect(this, SIGNAL(writeRequest(QByteArray)),
this, SLOT(writeSerial(QByteArray)));
}
After the message to send in currentStatus has been prepared, emit writeRequest(msg); is called. This dispatches a signal that is connected to the slot writeRequest. writeRequest is used to setup and actually write the message to the serial port.
void Device::writeSerial(const QByteArray &msg){
if (serial.portName() != "COM3") {
serial.close();
serial.setPortName("COM3");
if (!serial.open(QIODevice::ReadWrite)) {
processError(tr("Can't open %1, error code %2")
.arg(serial.portName()).arg(serial.error()));
return;
}
if (!serial.setBaudRate(QSerialPort::Baud19200)) {
processError(tr("Can't set rate 19200 baud to port %1, error code %2")
.arg(serial.portName()).arg(serial.error()));
return;
}
if (!serial.setDataBits(QSerialPort::Data8)) {
processError(tr("Can't set 8 data bits to port %1, error code %2")
.arg(serial.portName()).arg(serial.error()));
return;
}
if (!serial.setParity(QSerialPort::NoParity)) {
processError(tr("Can't set no patity to port %1, error code %2")
.arg(serial.portName()).arg(serial.error()));
return;
}
if (!serial.setStopBits(QSerialPort::OneStop)) {
processError(tr("Can't set 1 stop bit to port %1, error code %2")
.arg(serial.portName()).arg(serial.error()));
return;
}
if (!serial.setFlowControl(QSerialPort::NoFlowControl)) {
processError(tr("Can't set no flow control to port %1, error code %2")
.arg(serial.portName()).arg(serial.error()));
return;
}
}
qDebug() << "Message written";
this->msgRequest = msg;
serial.write(msgRequest);
serialTimer.start(400);
}
After setting up the serial port, I save the current message to msgRequest. This may have to be used to resend the message if there is an error. After serial.write() is called, I setup a timer for 400ms. Once this timer expires, I check what was read from the serial port.
handleResponse() is a slot that is called everytime QSerialPort emits the readyRead() signal. readyRead() appends any available data to the QByteArray response.
void Device::handleResponse(){
response.append(serial.readAll());
}
After 400ms, serialTimer (one shot Timer) will emit a timeout() signal. serialTimer was started right after writing our requested message to the serial port. processTimeout() is where we finally check the response received from the PowerLinc Modem after sending our message. When messages are sent to the INSTEON PowerLinc Modem (PLM), the PLM echoes back the message and appends either 0x06 (Positive ACK) or 0x15 (NACK). In processTimeout() I check to make sure the last byte received is the ACK byte, if not - resend our originally requested message.
void Device::processTimeout(){
qDebug() << "Read: " << response.toHex();
int msgLength = this->msgRequest.length();
if(response.at(msgLength)!=0x06){
qDebug() << "Error, resend.";
emit writeRequest(msgRequest);
}
response.clear();
}
I used the Serial Port Monitor 4.0 (Eltima Software) to verify the write and read transactions on the serial port. Below, you can see the log printout for 1 sample transaction.
20:44:30:666 STATUS_SUCCESS 02 62 1d e9 4b 05 19 00 <--- Send
20:44:30:669 STATUS_SUCCESS 02 62 1d e9 4b 05 19 00 06 <--- Receive
20:44:30:875 STATUS_SUCCESS 02 <--- Receive
20:44:30:881 STATUS_SUCCESS 50 1d e9 4b 1e da f7 21 00 ff <--- Receive
For 20 sends, I received the same response. Thus, I can safely say my issues with inconsistent data arrival have been resolved. Now I am struggling with multiple write requests, but I believe that is a separate question to be investigated. I appreciate everyone's support.
See the BlockingMaster example in the repository and read the documentation about the blocking I/O. Also, do not use blocking I/O unnecessarily.
Use bytesAvailable() to get the number of available data for reading, because not the fact that you immediately receive a complete response package.

Use format strings that contain %1, %2 etc. instead of %d, %s etc. - Linux, C++

As a follow-up of this question (Message compiler replacement in Linux gcc), I have the following problem:
When using MC.exe on Windows for compiling and generating messages, within the C++ code I call FormatMessage, which retrieves the message and uses the va_list *Arguments parameter to send the varied message arguments.
For example:
messages.mc file:
MessageId=1
Severity=Error
SymbolicName=MULTIPLE_MESSAGE_OCCURED
Language=English
message %1 occured %2 times.
.
C++ code:
void GetMsg(unsigned int errCode, wstring& message,unsigned int paramNumber, ...)
{
HLOCAL msg;
DWORD ret;
LANGID lang = GetUserDefaultLangID();
try
{
va_list argList;
va_start( argList, paramNumber );
const TCHAR* dll = L"MyDll.dll";
_hModule = GetModuleHandle(dll);
ret =::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE|FORMAT_MESSAGE_IGNORE_INSERTS,
_hModule,
errCode,
lang,
(LPTSTR) &msg,
0,
&argList );
if ( 0 != ret )
{
unsigned int count = 0 ;
message = msg;
if (paramNumber>0)
{
wstring::const_iterator iter;
for (iter = message.begin();iter!=message.end();iter++)
{
wchar_t xx = *iter;
if (xx ==L'%')
count++;
}
}
if ((count == paramNumber) && (count >0))
{
::LocalFree( msg );
ret =::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_HMODULE,
_hModule,
errCode,
GetUserDefaultLangID(),
(LPTSTR) &msg,
0,
&argList );
}
else if (count != paramNumber)
{
wstringstream tmp;
wstring messNumber;
tmp << (errCode & 0xFFFF);
tmp >> messNumber;
message = message +L"("+ messNumber + L"). Bad Format String. ";
}
}
::LocalFree( msg );
}
catch (...)
{
message << L"last error: " << GetLastError();
}
va_end( argList );
}
Caller code:
wstring message;
GetMsg(MULTIPLE_MESSAGE_OCCURED, message,2, "Error message", 5);
Now, I wrote a simple script to generate a .msg file from the .mc file, and then I use gencat to generate a catalog from it.
But is there a way to use the formatted strings as they contain %1, %2, etc. and NOT the general (%d, %s...) format?
Please note, that the solution has to be generic enough for each possible message with each posible types\ arguments order...
Is it possible at all?
Thank you.
First of all functions like printf support positioned format:
printf("%2$s, %1$d", salary, name);
For C++, beside the C solution there is a boost::format library:
std::cout << boost::format("%2%, %1%") % salary % name;
Also if you are moving software to Linux I would suggest use "different" approach for localization: use either gettext or boost.locale library.
And instead of this:
wstring message;
GetMsg(MULTIPLE_MESSAGE_OCCURED, message,2, "Error message", 5);
Use :
C/gettext:
snprintf(msg,sizeof(msg),gettext("This is the message to %1$s about %2$s"),who,what);
C++/gettext:
using boost::format;
std::ostringstream ss;
ss << format(gettext("This is the message to %1% about %2%")) % who % what;
C++ using boost.locale:
using boost::locale::format;
using boost::locale::translate;
std::ostringstream ss;
ss << format(translate("This is the message to {1} about {2}")) % who % what;
The FormatMessage() function does use printf-style format specifiers; they go inside exclamation points after the %1 or whatever. A placeholder with no format specifier is equivalent to a printf "%s."
What you'd need to do would be to transform the format strings a bit; change "%1" to "%1$s", "%2!u!" to "%2$u", "%3!4.5e!" to "%3$4.5e" and so on. Basically just change the ! characters around the format specifier into a single $ preceding it, and cope with the possibility of a bare "%number".
The problem with positional parameters like %1$anytype is that it must appear in the format string for a %2$anytype to work. Cf. printf("Today, %1$s received %2$d dollars in salary\n", name, salary); vs. printf("Today, I received %2$d dollars in salary\n", name, salary); (boom). So it does not always work, like when the user is free to provide the format string, and decides to omit a field. In that case, a named parameter approach seems preferable. libHX for example provides such where you could use "%(SALARY) %(NAME)".