【问题标题】:c++ function that takes any data type without using templates?c++ 函数在不使用模板的情况下采用任何数据类型?
【发布时间】:2014-09-30 13:31:23
【问题描述】:

我有一个任务要求为任何数据类型编写一个函数。该函数应该打印结构的字节并识别数据结构使用的字节总数以及区分用于成员和字节的字节用于填充。

我的直接反应以及大多数班级的反应是使用模板。这允许您编写一次函数并收集传递给函数的对象的运行时类型。使用 memset 和 typeid 可以轻松完成所要求的内容。然而,我们的教授。刚刚看到我们关于模板和该死的模板的讨论。

看到这个后,我陷入了困境,我正在寻找一些指导作为解决这个问题的最佳方法。我调查过的一些事情:

  • 带有显式强制转换的空指针(这似乎会变得一团糟)
  • 具有仅从其继承所有数据结构的虚函数的基类,这样做似乎有点奇怪。
  • 对我们的每个数据结构都有“友谊”的基类。
  • 为我们问题集中的每个数据结构重写一个函数(我想这是最糟糕的解决方案)。

希望我忽略了一个常见的 c++ 工具,有人有什么想法吗?

【问题讨论】:

  • 如果您的 C++ 讲师将模板搞砸了,我真诚地希望现在为其他讲师放弃课程还为时不晚。可以肯定的是,有些东西应该被诅咒,但它不是模板。
  • 即使使用模板也是不可能的。编译器(除非有一些非标准扩展)没有提供足够的运行时信息来确定类型布局的确切细节,而无需了解它。
  • 你的目标到底是什么?将结构的原始内存分解为成员字节和填充字节?
  • 您的讲师是否在寻找通用的独立于平台的解决方案?还是有一些 特定的 (例如 MS 编译器的调试输出,它在编译期间抛出布局信息)。后一个要求,通过offsetof 进行情感剖析,正如@DarkFalcon 所说,除非您连接到编译过程本身,否则如果不指定代码中的成员,您就无法做到这一点。
  • 即使您使用模板,memset 将如何帮助您?

标签: c++ templates


【解决方案1】:

尽可能地把函数当作愚蠢的,事实上,把它当作什么都不知道,所有的信息都必须传递给它。

函数参数:

  • 结构地址,为uint8_t *。 (需要打印字节)
  • 结构大小,以字节为单位。 (需要打印字节并打印 总大小)
  • 成员信息向量:成员长度或成员使用的字节总和。

需要该向量来满足打印成员使用的字节和填充使用的字节的要求。您可以选择传递成员的总和。

例子:

  void Analyze_Structure(uint8_t const *  p_structure,
                         size_t           size_of_structure,
                         size_t           size_occupied_by_members);

这个赋值的技巧是弄清楚如何让调用函数确定这些项目。

希望这会有所帮助。

编辑 1:

struct Apple
{
  char    a;
  int     weight;
  double  protein_per_gram;
};

int main(void)
{
  Apple granny_smith;
  Analyze_Structure((uint8_t *) &granny_smith,
                    sizeof(Apple),
                    sizeof(granny_smith.a)
                    + sizeof(granny_smith.weight)
                    + sizeof(granny_smith.protein_per_gram);
  return 0;
}

【讨论】:

  • "as a uint8_t *. (需要打印字节)" 可能违反严格的别名规则。 char 类型也有例外。
  • sizeof(Apple::a) 是有效的 C++(自 C++11 起),不需要对象。
  • @dyp 你的意思是一个有效的C++11
  • sizeof(granny_smith)==sizeof(granny_smith.a)+sizeof(granny_smith.weight)+sizeof(granny_smith.protien_per_gram),提供granny_smith是值类型而不是引用类型。
  • @MadScienceDreams 不,你不考虑填充。
【解决方案2】:

我有一个任务要求为任何数据类型编写一个函数。

这意味着模板(您的教授已解散)、void* 或可变数量的参数(类似于 printf)。

该函数应该打印结构的字节

void your_function(void* data, std::size_t size)
{
    std::uint8_t* bytes = reinterpret_cast<std::uint8_t*>(data);

    for(auto x = bytes; x != bytes + size; ++x)
        std::clog << "0x" << std::hex << static_cast<std::uint32_t>(*x) << " ";
}

[...] 并识别数据结构使用的总字节数,并区分用于成员的字节和用于填充的字节。

在这一点上,我迷路了:用于填充的字节(根据定义)不是结构的一部分。考虑:

struct x { char c; char d; char e; }; // sizeof(x) == 3;

x instance{ 0, 0, 0 };
your_function(&instance, sizeof(x)); // passes 3, not 4 (4 for 32bits architecture)

理论上,您也可以将alignof(instance) 传递给函数,但这不会告诉您内存中字段的对齐方式(据我所知,它不是标准化的,但我可能错了)。

这里有几种可能性:

  1. 你的教授。学习了 10 或 20 年前被认为是好代码的“hacky”C++,并且没有更新他的知识(C 风格的代码、指针、直接内存访问和“智能黑客”都在这里)。

  2. 他不知道如何准确表达他想要的内容或使用的术语(“为任何数据类型编写函数”太含糊了:作为开发人员,如果我接到这个任务,首先要做的就是是询问细节 - 例如“它将如何使用?”和“预期的函数签名是什么”)。

    例如,这可以在一定程度上通过宏来实现,但如果他希望您使用宏来代替函数和模板,您可能应该考虑更换教授。

  3. 他的意思是你应该写一些任意的数据类型(比如我上面的struct x)并围绕它定义你的API(不太可能)。

【讨论】:

  • “用于填充的字节(根据定义)不是结构的一部分” 我不确定你的意思。 sizeofalignof 必须与指针算法兼容(在数组内),因此任何填充都显示在 sizeof 结构中。
  • 填充字节是结构的一部分。只是在您的示例中不需要填充(在大多数架构中)。尝试使用 struct test { char i;诠释 j; };为我输出 8 个。
【解决方案3】:

我不确定这样的函数是否可以在没有最少内省的情况下构建:您需要知道结构成员是什么,否则您只能访问结构的大小。

无论如何,如果代码的用户“合作”,这是我的建议,该解决方案无需内省即可工作。

您的函数将作为结构体地址和 sizeof 的参数 void* 和 size_t。

0) 让用户创建所需类型的结构。

1) 让用户调用你的函数,将所有字节设置为 0。

2) 让用户为结构的每个字段赋值。

3) 让用户调用您的函数,该函数记录仍然为 0 的每个字节。

4) 让用户调用你的函数,将所有字节设置为 1。

5) 让用户再次为结构的每个字段赋值。 (与第一次相同的值!)

6) 让用户调用您的函数并计算仍然为 1 且之前已标记的字节数。这些是填充字节。

尝试使用值 0 然后 1 的原因是用户分配的值可能包含字节 0;但它们不能同时是字节 0 和字节 1,因此其中一项测试将排除它们。

struct _S { int I; char C } S;

Fill0(S, sizeof(S));

// User cooperation
S.I= 0;
S.C= '\0';

Mark0(S, sizeof(S)); // Has some form of static storage

Fill1(S, sizeof(S));

// User cooperation
S.I= 0;
S.C= '\0';

DetectPadding(S, sizeof(S));

您可以将所有这些打包在一个函数中,该函数接受一个回调函数参数来执行成员分配。

void Assign(void* pS) // User-written callback
{
  struct _S& S= *(struct _S)pS;

  S.I= 0;
  S.C= '\0';
}

【讨论】:

    猜你喜欢
    • 2021-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多