在不使用C ++中字段名称的情况下打印实例化结构的值

时间:2016-06-19 23:42:24

标签: c++ string struct

我可以这样做吗?

例如,考虑以下结构:

struct bag {
     string fruit;
     string book;
     string money;
};

我想以顺序的形式打印结构包实例的字段值,并获得如下输出:

apple
Computer Networking, A top-down Approach
100

但不使用字段的名称(水果,书籍和金钱)。任何帮助,将不胜感激。我知道的唯一信息是所有字段都是C ++字符串。

4 个答案:

答案 0 :(得分:3)

虽然C ++没有反射,但您可以使用Boost.Hana创建自己的反射工具。这是一个完整的程序,它迭代结构的成员,打印它们的名称和值。

Hana需要一个符合C ++ 14标准的现代编译器,这意味着最新版本的Clang或GCC 6+是您现在唯一的选择。

修改:此代码现在使用BOOST_HANA_ADAPT_STRUCT代替BOOST_HANA_ADAPT_ADT

#include <boost/hana/adapt_struct.hpp>
#include <boost/hana/for_each.hpp>
#include <boost/hana/fuse.hpp>
#include <string>
#include <iostream>

namespace hana = boost::hana;

using std::string;

struct bag {
    string fruit;
    string book;
    string money;
};

BOOST_HANA_ADAPT_STRUCT(bag, fruit, book, money);

int main() {

    bag my_bag{ "Apple", "To Kill A Mockingbird", "100 doubloons" };

    hana::for_each(my_bag, hana::fuse([](auto member, auto value) {
        std::cout << hana::to<char const*>(member) << " = " << value << "\n";
    }));
}

输出:

fruit = Apple
book = To Kill A Mockingbird
money = 100 doubloons

答案 1 :(得分:2)

根据您在评论中的澄清:这在C ++中是不可能的。 C ++ this。 C ++不能这样工作。

答案 2 :(得分:2)

如果你只想在自己定义的结构上使用它,你可以使用Boost Fusion来做到这一点。你会像这样声明你的结构:

BOOST_FUSION_DEFINE_STRUCT(
(your_namespace1)(your_namespace2),
    bag,
        (string, fruit),
        (string, book),
        (string, money)
)

使用一个丑陋的宏有点烦人,但在一天结束时会产生相同的结构,加上很多其他的东西,并且至少没有重复

一旦您以这种方式宣布您的结构,它现在被认为是Boost Fusion序列,您可以使用Boost Fusion的各种工具对其进行操作,包括for_each允许您使用依次将泛型函数应用于结构中的每个元素。

#include <boost/fusion/include/for_each.hpp>

bag b1;
// populate bag
boost::fusion::for_each(b1, [] (const auto& x) { std::cout << x << "\n";});

这将打印出您线路上的每个字段。你可以用这种方式很好地做各种事情。作为奖励,这将适用于没有任何额外工作的非字符串字段,因此您可以例如赚钱而不是(这可能更有意义,尽管我无法从问题中说出来)。

编辑:我看到那个巴雷特和我同时发布了。 Hana是一个更新,更现代的库,它超越了Boost Fusion的功能(我相信)。如果你可以使用Hana,一定要继续,但它的一些编译要求是苛刻的,特别是如果你想要目标gcc,世界上大部分仍然在5系列。目前还无法与其他许多编译器进行编译,但是icc和msvc根本不会工作。对于这样一个直截了当的问题,你可以用Fusion解决它,老实说,今天就是我推荐的。一年后,希望我的推荐会有所不同。

答案 3 :(得分:-1)

由于您知道所有字段都是std::string,并且由于您可能控制了聚合类,因此在实践中可以简单地将其视为std::string的原始数组:

Bag x;
static constexpr int n = sizeof(x)/sizeof(std::string);
for( int i = 0; i < n; ++i )
{
    cout << reinterpret_cast<std::string const*>( &x )[i] << "\n";
}

如果您知道有多少字段,那么您可以静态断言类的总大小,从而保证编译器没有添加任何填充(允许在第一个字段之前的任何位置添加填充,必须在偏移0):

#define STATIC_ASSERT( e ) static_assert( e, #e " // <- must be true" )

STATIC_ASSERT( sizeof( Bag ) == 3*sizeof( std::string ) );

否则将其视为一个数组正式未定义行为,但我不认为可以找到一个默认情况下会添加任何填充的编译器,因此它应该在实践中非常便携。

上述免责声明:未经编译审核的代码。

如果你真的想要安全,甚至不是迂腐形式的UB,如果这超过了根本不使用项目名称的要求,那么只需在每个结构旁边声明必要的迭代知识,例如: :

#include <array>
#include <string>
using namespace std;

struct Bag
{
     string fruit;
     string book;
     string money;
};

array<string Bag::*, 3> const bag_items = {&Bag::fruit, &Bag::book, &Bag::money};

#include <iostream>
auto main()
    -> int
{
    Bag const bag = { "Apple", "Zen and TAOMM", "billion bucks" };
    for( auto mp : bag_items )
    {
        cout << bag.*mp << "\n";
    }
}

对于异构结构(不同类型的项),您自然会使用函数而不是直接成员指针。

但是正如这表明的那样,即使一个人完全害怕正式的UB,也没有必要在像Boost这样的大型库中添加一个依赖:这样做很简单。