使用C ++模板与C结构的反省?(Using C++ Templates with C struct

2019-07-30 07:12发布

我在做C ++的一些工作有一切写在C公司(用C不是我:(可选)。他们有一些数据结构非常相似(即,它们都具有领域如“姓名”,“地址”,等等。但是,不管是什么原因没有他们用于基础一切断(使得做什么地狱)共同的结构。Anywho,我需要做一个全系统这些结构是在内存中,并通过它所有到一个表的分析。不算太差,但表必须包括所有变量的所有领域的项目,即使他们没有现场(结构b可有场“潜伏”,而是一个结构不 - 在表中的每个实例的条目必须为“延迟”的空项。

所以,我的问题是,有没有一种方法来确定在运行时如果已经传递到模板函数的结构有一个特定的领域? 或者将我必须写一些黑魔法宏,它会为我吗? (这个问题基本上是我不能使用模板特)

谢谢! 如果您有任何疑问,请随时问!

这里是什么,我在想一个SNIPPIT ...

struct A
{
  char name[256];
  int index;
  float percision;
};

struct B
{
  int index;
  char name[256];
  int latency;
};

/* More annoying similar structs... note that all of the above are defined in files that were compiled as C - not C++ */

struct Entry
{
  char name[256];
  int index;
  float percision;
  int latency;
  /* more fields that are specific to only 1 or more structure */
};

template<typename T> struct Entry gatherFrom( T *ptr )
{
  Entry entry;

  strcpy( entry.name, ptr->name, strlen( ptr->name ) );
  entry.index = ptr->index;
  /* Something like this perhaps? */
  entry.percision = type_contains_field( "percision" ) ? ptr->percision : -1;
}

int main()
{
  struct A a;
  struct B b;

  /* initialization.. */

  Entry e  = gatherFrom( a );
  Entry e2 = gatherFrom ( b );

  return 0;
}

Answer 1:

一切用C写的(用C不是我的选择:()。

首先,我想引述Linus Torvalds的不得不说一下这个问题:


From: Linus Torvalds <torvalds <at> linux-foundation.org>
Subject: Re: [RFC] Convert builin-mailinfo.c to use The Better String Library.
Newsgroups: gmane.comp.version-control.git
Date: 2007-09-06 17:50:28 GMT (2 years, 14 weeks, 16 hours and 36 minutes ago)

C++ is a horrible language. It's made more horrible by the fact that a lot 
of substandard programmers use it, to the point where it's much much 
easier to generate total and utter crap with it. Quite frankly, even if 
the choice of C were to do *nothing* but keep the C++ programmers out, 
that in itself would be a huge reason to use C.

http://harmful.cat-v.org/software/c++/linus


他们有一些数据结构非常相似(即,它们都具有字段,如“名”,“地址”,等等。但是,不管是什么原因没有他们用于基础一切掀起了共同的结构的(使得做什么地狱)。

他们可能有过这种非常充分的理由。 把共用字段成一个单一的基底结构(类)可以听起来像一个好主意。 但它使您是否要发生重大变化应用到结构的一个事情真的很难,同时不影响剩余(更换某些领域,改变类型等)。 OOP是肯定不会做的事情的唯一正确的方法。

所以,我的问题是,有没有一种方法来确定在运行时如果已经传递到模板函数的结构有一个特定的领域?

不,这是不可能的。 无论用C还是在C ++中,因为在创建二进制文件时关于类型的所有信息被丢弃。 有既不反射也不反省C或C ++。 当然,从技术上编译器发出的调试信息确实提供了这一信息,但没有语言内置功能来访问此。 同时这种调试信息依赖于在编译的时候进行分析,而不是在运行时。 C ++有RTTI,但是这仅是一个非常粗糙的系统来识别一个实例是关闭的哪个类。 它不与类或结构成员的帮助。

但是,为什么你关心在运行时无论如何要做到这一点?

Anywho,我需要做的这些结构是在内存中的一个全系统的分析,并通过这一切到一个表。

你应该真正高兴的是,你要分析C和不是C ++。 因为C是真的,真的很容易解析(不像C ++这是非常困难解析,主要是因为那些该死的模板)。 特别是结构。 我只写一个小而简单的脚本,提取从C源的所有结构定义。 但是,由于结构是固定大小的,他们往往包含指向动态分配的数据。 除非你想修补分配,我想分析这个最简单的方法,是通过挂接到一个调试器和记录每一个独特的对象,其指针被分配到一个结构成员的内存使用情况。



Answer 2:

你可以在编译时做到不触及原有结构的来源:

#include <iostream>
#include <limits>
#include <memory.h>

struct A
{
    char name[256];
    int index;
    float percision;
};

struct B
{
    int index;
    char name[256];
    int latency;
};

struct Entry
{
    char name[256];
    int index;
    float percision;
    int latency;
    /* more fields that are specific to only 1 or more structure */
};

inline
std::ostream & operator<<(std::ostream & os, Entry const & e) {
    return os << e.name << "{" << e.index << ", " << e.percision << ", " << e.latency << "}";
}

template <typename T>
inline
void assign(T & dst, T const & src) {
    dst = src;
}

template <size_t N>
inline
void assign(char (&dst)[N], char const (&src)[N]) {
    memcpy(dst, src, N);
}

#define DEFINE_ENTRY_FIELD_COPIER(field)                            \
    template <typename T>                                           \
    inline                                                          \
    decltype(T::field, true) copy_##field(T const * t, Entry & e) { \
        assign(e.field, t->field);                                  \
        return true;                                                \
    }                                                               \
                                                                    \
    inline                                                          \
    bool copy_##field(void const *, Entry &) {                      \
            return false;                                           \
    }

DEFINE_ENTRY_FIELD_COPIER(name)
DEFINE_ENTRY_FIELD_COPIER(index)
DEFINE_ENTRY_FIELD_COPIER(percision)
DEFINE_ENTRY_FIELD_COPIER(latency)

template <typename T>
Entry gatherFrom(T const & t) {
    Entry e = {"", -1, std::numeric_limits<float>::quiet_NaN(), -1};
    copy_name(&t, e);
    copy_index(&t, e);
    copy_percision(&t, e);
    copy_latency(&t, e);
    return e;
}

int main() {
    A a = {"Foo", 12, 1.2};
    B b = {23, "Bar", 34};

    std::cout << "a = " << gatherFrom(a) << "\n";
    std::cout << "b = " << gatherFrom(b) << "\n";
}

所述DEFINE_ENTRY_FIELD_COPIER()宏定义为要提取的每个字段中的对重载函数。 一个过载( copy_##field(T const * t, …)其成为copy_name(T const * t, …) copy_index(T const * t, …)等)返回类型定义为decltype(T::field, true) ,这解决了键入bool如果T具有称为一个数据成员nameindex等等。如果T不具有这样的领域中,替换将失败,但不是使编译时间错误,在该第一过载被简单地视为好像不存在,(这被称为SFINAE ),因此呼叫解析到第二过载, copy_##field(void const * t, …)其在所有接受任何类型的第一个参数而什么都不做。

笔记:

  1. 因为这个代码解析在编译时的过载, gatherFrom()是最佳的,在这个意义上,对于所生成的二进制代码gatherFrom<A>()例如,将显示为如果调谐它为A手工:

     Entry handCraftedGatherFromA(A const & a) { Entry e; e.latency = -1; memcpy(_result.name, a.name, sizeof(a.name)); e.index = a.index; e.percision = a.percision; return e; } 

    下克++ 4.8与-O3gatherFrom<A>()handCraftedGatherFromA()生成相同的代码:

     pushq %rbx movl $256, %edx movq %rsi, %rbx movl $-1, 264(%rdi) call _memcpy movss 260(%rbx), %xmm0 movq %rax, %rcx movl 256(%rbx), %eax movss %xmm0, 260(%rcx) movl %eax, 256(%rcx) movq %rcx, %rax popq %rbx ret 

    锵4.2的gatherFrom<A>()不做为好,不幸的是, 它冗余零初始化整个条目。 因此,它是不是所有的玫瑰,我猜。

    通过使用NRVO ,这两个版本避免复制e返回时它。 不过,我要指出,两个版本将节省一个操作码( movq %rcx, %rax通过使用输出参数,而不是返回值)。

  2. copy_…()函数返回一个bool指示副本是否发生或不结果。 这是当前未使用,但它可以使用,例如,定义int Entry::validFields作为指示进行填充哪些字段位掩码。

  3. 宏不是必需的; 它只是干 。 基本成分是使用SFINAE的。

  4. assign()重载也并不是必须的。 他们只是避免具有不同的几乎相同的宏来处理字符数组。

  5. 上面的代码依赖于C ++ 11的decltype关键字。 如果您使用的是老式的编译器,它的混乱,但还是可以的。 我已经成功地拿出干净的解决方案如下。 它的C ++ 98的符合性,并仍然是基于SFINAE原则:

     template <typename C, typename F, F (C::*), typename T> struct EnableCopy { typedef T type; }; #define DEFINE_ENTRY_FIELD_COPIER(field, ftype) \ template <typename T> \ inline \ typename EnableCopy<T, ftype, &T::field, bool>::type \ copy_##field(T const * t, Entry & e) { \ copy_value(e.field, t->field); \ return true; \ } \ \ inline \ bool copy_##field(void const *, Entry &) { \ return false; \ } DEFINE_ENTRY_FIELD_COPIER(name , char[256]); DEFINE_ENTRY_FIELD_COPIER(index , int); DEFINE_ENTRY_FIELD_COPIER(percision, float); DEFINE_ENTRY_FIELD_COPIER(latency , int); 

    您还可以放弃C ++ 11的便携式std::numeric_limits<float>::quiet_NaN()并使用一些技巧( 0.0f/0.0f似乎工作)或选择其他魔法值。



Answer 3:

是的,这是不难的。 只要把两者的AEntry单个对象,使Entry一个二等公民:

void setDefaultValues(Entry*); // You should be able to provide these.
struct Entry {
  int x;
  int y;
};
struct Indirect : public Entry { };
template<typename T> struct EntryOr : public T, Indirect
{
  setDefaultValues(this);
};

// From C code
struct A {
  int x;
}

int main()
{
  EntryOr<A> foo;
  foo.x = 5; // A::x
  std::cout << foo.x << foo.y; // Prints A::x and Entry::y
}

(链接)



文章来源: Using C++ Templates with C structs for introspection?