类成员变量的自定义顺序输出问题

catxl313 2012-06-19 09:59:59
伪代码如下:

#include <iostream>
using namespace std;

class A
{
public:
int i;
double d;
char* c;
string s;
int print(); // 按照配置文件中定义的顺序打印
};

/*
* 我想在配置文件中定义输出变量及其顺序,比如:
* format=${i}|${s}|${d}|
* 注意:某些变量${c}没有出现在配置里,表示不打印。
*/

LOAD_CONFIG(); // 加载配置
A a;
a.print(); // 按照配置文件中定义的顺序打印




请问该如何实现?

谢谢!
...全文
248 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
cofday 2012-06-26
  • 打赏
  • 举报
回复
应该是这样的:
不知道有米有解决楼主的问题?
#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;
enum ItemType
{
TYPE_INT,
TYPE_DOUBLE,
TYPE_STLSTRING,
};
template<class T>
struct ItemFormat
{
const char *name; // 变量名
ItemType tp; // 变量类型
const char *fmt; // 格式化符号
//size_t offset; // 类型偏移
char T::* poAdress; // 类型偏移
};
#define PREFIX '$'
#define BUFFSZ 1024
class A
{
public:
int i;
double d;
std::string s;
//
// 所有可用的配置变量
//
static ItemFormat<A> FormatDef[];
//
// format=hello|${i}|${s}|${d}|world
//
static char FormatBuff[BUFFSZ]; // 加载上述配置后变成:hello|$|$|$|world
static std::vector<int> ItemOrder; // 加载上述配置后变成:[0,1,2], 数字分别表示上述 $ 符号对应在 FormatDef 里的下标
static int SetFormat(const char* fmt);
int printfA(char* buff, int bufflen, ItemFormat<A> *fmt, int sz);
};
ItemFormat<A> A::FormatDef[] = {
{"i", TYPE_INT, "%d", (char A::*)&A::i},
{"s", TYPE_STLSTRING, "%s", (char A::*)&A::s},
{"d", TYPE_DOUBLE, "%f", (char A::*)&A::d},
{0,TYPE_INT,0,0},
};
//
// format=hello|${i}|${s}|${d}|world
//
char A::FormatBuff[BUFFSZ] = {0};
std::vector<int> A::ItemOrder;
//
// 格式配置加载、转换由 SetFormat 完成
//
int A::SetFormat(const char* fmt)
{
int i = 0;
int k = 0;
for(i=0; (i<sizeof(FormatBuff)) && (0!=fmt[i]); i++)
{
FormatBuff[k++] = fmt[i];
if(PREFIX==fmt[i]) // xxxx${varname}zzzz
{
++i;
if( (i<sizeof(FormatBuff)) && ('{'==fmt[i]) )
{
++i; // begin pos of 'varname'
char varname[32] = {0};
int j = 0;
for(j=0; (j<sizeof(varname)) && (0!=fmt[i]) && ('}'!=fmt[i]); j++)
{
varname[j] = fmt[i];
++i;
}
if('}'!=fmt[i]) // 最后的字符必定是'}', 否则格式有误
return -1;
int itemid = -1;
// 找到该变量在 FormatDef 中的位置
for(j=0; 0!=FormatDef[j].name; j++)
{
if( 0==strcmp(FormatDef[j].name, varname) )
{
itemid = j;
break;
}
}
if(itemid>=0)
ItemOrder.push_back(itemid);
}
}
}
return 0;
}
int A::printfA(char* buff, int bufflen, ItemFormat<A> *fmt, int sz)
{
char *p = buff;
int i = 0;
std::vector<int>::const_iterator it = ItemOrder.begin();
for(i=0; ((i<sizeof(FormatBuff)) && (0!=FormatBuff[i]) && (p-buff)<bufflen); i++)
{
if( PREFIX==FormatBuff[i] )
{
if(it!=ItemOrder.end())
{
int len = 0;
int idx = *it;
if( idx>=0 && idx<=sz && NULL!=fmt[idx].name )
{
switch(fmt[idx].tp)
{
case TYPE_INT:
//len = sprintf(p, fmt[idx].fmt, *((int*)((char*)this+fmt[idx].offset)));
len = sprintf(p, fmt[idx].fmt, this->*(fmt[idx].poAdress));
break;
case TYPE_DOUBLE:
//len = sprintf(p, fmt[idx].fmt, *((double*)((char*)this+fmt[idx].offset)));
len = sprintf(p, fmt[idx].fmt, this->*((double A::*)(fmt[idx].poAdress)));
break;
case TYPE_STLSTRING:
//len = sprintf(p, fmt[idx].fmt, ((std::string*)((char*)this+fmt[idx].offset))->c_str());
len = sprintf(p, fmt[idx].fmt, (this->*((std::string A::*)(fmt[idx].poAdress))).c_str());
break;
default:
break;
}
if( len>0 )
{
p += len;
}
else // failed when do sprintf
{
memset(buff, 0, bufflen);
return -1;
}
}
else
{
return -1;
}
it++;
}
else
{// $, 但是没有格式化信息,即 $ 的数量太多
memset(buff, 0, bufflen);
return -1;
}
}
else
{
*p = FormatBuff[i];
++p;
}
}
if((0!=FormatBuff[i]) || (p-buff)>bufflen)
{
memset(buff, 0, bufflen);
return -1;
}
return 0;
}
int main(int argc, char* argv[])
{
A::SetFormat("hello|${i}|${s}|${d}|world");
A a;
a.i = 1;
a.s = "s";
a.d = 3.1415;
char buff[1024] = {0};
a.printfA(buff, 1024, A::FormatDef, sizeof(A::FormatDef)/sizeof(ItemFormat<A>));
cout << buff << endl;
}

pathuang68 2012-06-21
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]

顶一下
期待解决方案。。。
[/Quote]

参考:

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

class A
{
public:
int i;
double d;
char* c;
string s;

void print(char* config) // 按照配置文件中定义的顺序打印
{
while(strlen(config) > 0)
{
if(*config == 'i')
{
cout << i << " ";
}

if(*config == 'd')
{
cout << d << " ";
}

if(*config == 'c')
{
cout << c << " ";
}

if(*config == 's')
{
cout << s << " ";
}

config++;
}
cout << endl;
}
};

int main(int argc, char* argv[])
{
char config[100] = {0};

// 从文件读出数据
ifstream fis("E:/config.dat");
if(!fis)
{
cout << "can not open file..." << endl;
exit(1);
}

fis.getline(config, 100);

fis.close();

A a = {1, 2.2, 0, "hello"};
a.print(config);

return 0;
}


如果在e盘跟目下有一个文件config.dat,其内容是
ids
的话,那么就会输出
1 2.2 hello

如果config.dat的内容是
isd
的话,那么就会输出
1 hello 2.2
catxl313 2012-06-21
  • 打赏
  • 举报
回复
非常感谢【玄机逸士】的热心帮助。

不可否认,这段代码可以正常工作,但我希望有更快的解决方案:不希望有这么多循环匹配的过程,我的思路是,希望在:
LOAD_CONFIG(); // 加载配置
的过程中一次生成好一些中间数据,然后我用这些中间数据可以很快的生成输出格式,因为我需要输出的内容有几十项,不仅仅是“isd”这样3个,所以如果用你的方法,那么我将在输出的时候每次都要经过很多次的比较。

从你的思路出发,直接的优化思路是对“isd”进行hash索引,但又遇到索引内容类型不明确的问题。


直接贴我的代码吧,这个程序编译都过不去,就因为遇到了“类成员变量指针”的问题,换成struct就好了,但我希望找到一个C++的解决办法,如果没人给出更好的解决方案,分数就全给【玄机逸士】了:

#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;

enum ItemType
{
TYPE_INT,
TYPE_DOUBLE,
TYPE_STLSTRING,
};

struct ItemFormat
{
const char *name; // 变量名
ItemType tp; // 变量类型
const char *fmt; // 格式化符号
size_t offset; // 类型偏移
};


class A
{
public:
int i;
double d;
std::string s;
static int SetFormat(const char* fmt);
int printfA(char* buff, int bufflen, ItemFormat *fmt, int sz);
};

//
// 所有可用的配置变量
//
ItemFormat g_ItemFormat[] = {
{"i", TYPE_INT, "%d", &A::i},
{"s", TYPE_STLSTRING, "%s", &A::s},
{"d", TYPE_DOUBLE, "%f", &A::d},
};

#define PREFIX '$'
//
// format=hello|${i}|${s}|${d}|world
//
static char g_fmt[1024] = {0}; // 加载上述配置后变成:hello|$|$|$|world
static std::vector<int> g_ItemOrder; // 加载上述配置后变成:[0,1,2], 数字分别表示上述 $ 符号对应在 g_ItemFormat 里的下标

//
// 格式配置加载、转换由 SetFormat 完成
//
int A::SetFormat(const char* fmt)
{
int i = 0;
for(i=0; (i<sizeof(g_fmt)) && (0!=fmt[i]); i++)
{
g_fmt[i] = fmt[i];
if(PREFIX==fmt[i]) // xxxx${varname}zzzz
{
++i;
if( (i<sizeof(g_fmt)) && ('{'==fmt[i]) )
{
++i; // begin pos of 'varname'
char varname[32] = {0};
int j = 0;
for(j=0; (j<sizeof(varname)) && (0!=fmt[i]) && ('}'!=fmt[i]); j++)
{
varname[j] = fmt[i];
++i;
}
if('}'!=fmt[i]) // 最后的字符必定是'}', 否则格式有误
return -1;
int itemid = -1;
// 找到该变量在 g_ItemFormat 中的位置
for(j=0; j<sizeof(g_ItemFormat)/sizeof(ItemFormat); j++)
{
if( 0==strcmp(g_ItemFormat[j].name, varname) )
{
itemid = j;
break;
}
}
if(itemid>=0)
g_ItemOrder.push_back(itemid);
}
}
}
return 0;
}

int A::printfA(char* buff, int bufflen, ItemFormat *fmt, int sz)
{
char *p = buff;
int i = 0;
std::vector<int>::const_iterator it = g_ItemOrder.begin();
for(i=0; ((i<sizeof(g_fmt)) && (0!=g_fmt[i]) && (p-buff)<sizeof(buff)); i++)
{
if( PREFIX==g_fmt[i] )
{
if(it!=g_ItemOrder.end())
{
int len = 0;
int idx = *it;
if( idx>=0 && idx<=sz && NULL!=fmt[idx].name )
{
switch(fmt[idx].tp)
{
case TYPE_INT:
len = sprintf(p, fmt[idx].fmt, *((int*)((void*)this+fmt[idx].offset)));
break;
case TYPE_DOUBLE:
len = sprintf(p, fmt[idx].fmt, *((double*)((void*)this+fmt[idx].offset)));
break;
case TYPE_STLSTRING:
len = sprintf(p, fmt[idx].fmt, (std::string*)((void*)this+fmt[idx].offset)->c_str());
break;
default:
break;
}

if( len>0 )
{
p += len;
}
else // failed when do sprintf
{
memset(buff, 0, bufflen);
return -1;
}
}
else
{
return -1;
}
it++;
}
else
{// $, 但是没有格式化信息,即 $ 的数量太多
memset(buff, 0, sizeof(buff));
return -1;
}
}
else
{
*p = g_fmt[i];
++p;
}
}

if((0!=g_fmt[i]) || (p-buff)>sizeof(buff))
{
memset(buff, 0, sizeof(buff));
return -1;
}
return 0;
}

int main(int argc, char* argv[])
{
A::SetFormat("hello|${i}|${s}|${d}|world");

A a;
a.i = 1;
a.s = "s";
a.d = 3.1415;

char buff[1024] = {0};
a.printfA(buff, 1024, g_ItemFormat, sizeof(g_ItemFormat)/sizeof(ItemFormat));

cout << buff << endl;
}

catxl313 2012-06-20
  • 打赏
  • 举报
回复
顶一下
期待解决方案。。。
catxl313 2012-06-19
  • 打赏
  • 举报
回复
对,配置文件怎么定义是小事,“isd”这样定义也可以。

我想知道具体在LOAD_CONFIG()中应该将这个 “isd” 转换成什么样的数据结构。
pathuang68 2012-06-19
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]

我想先定义一个全局变量:
ItemFormat itfmt[] = {
{"i", TYPE_STLSTRING, "%d", &A::i, 0},
{"c", TYPE_STLSTRING, "%s", &A::c, 0},
{"d", TYPE_UINT32, "%d", &A::d, 0},
{"s", TYPE_STLST……
[/Quote]

你清楚你定义的一个数组的含义吗?如果写一写连自己都不知道在干什么的代码,你怎么可以要求计算机懂?
pathuang68 2012-06-19
  • 打赏
  • 举报
回复
楼主,你是不是搞得有点复杂了?

以楼主给出的例子,文件直接用:isd这样写不就完了吗,搞那么多控制字符做甚?
catxl313 2012-06-19
  • 打赏
  • 举报
回复
我想先定义一个全局变量:
ItemFormat itfmt[] = {
{"i", TYPE_STLSTRING, "%d", &A::i, 0},
{"c", TYPE_STLSTRING, "%s", &A::c, 0},
{"d", TYPE_UINT32, "%d", &A::d, 0},
{"s", TYPE_STLSTRING, "%s", &A::s, 0},
{0, 0, 0, 0}
};

在LOAD_CONFIG()加载配置过程中,生成一个全局变量, 记录那些需要打印的变量以及打印顺序,然后在A::print()中使用这些预生成的结构。

那么上述结构体该如何定义?
catxl313 2012-06-19
  • 打赏
  • 举报
回复
我想先定义一个全局变量:
ItemFormat itfmt[] = {
{"i", TYPE_STLSTRING, "%d", &A::i, 0},
{"c", TYPE_STLSTRING, "%s", &A::c, 0},
{"d", TYPE_UINT32, "%d", &A::d, 0},
{"s", TYPE_STLSTRING, "%s", &A::s, 0},
{0, 0, 0, 0}
};

在LOAD_CONFIG()加载配置过程中,生成一个全局变量, 记录那些需要打印的变量以及打印顺序,然后在A::print()中使用这些预生成的结构。

那么上述结构体该如何定义?
catxl313 2012-06-19
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 的回复:]
你清楚你定义的一个数组的含义吗?如果写一写连自己都不知道在干什么的代码,你怎么可以要求计算机懂?
[/Quote]

好吧,你可以不管我试图怎么去实现,就看我第一楼贴的问题就行了。

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧