C ++:属于类实例的可命名对象,并存储在其中

时间:2013-09-20 12:53:00

标签: c++ indexing command-line-arguments stdstring compile-time

我试图让程序员(使用我的库)创建类型X的可命名实例,这些实例存储在类C的实例中(或者至少是那个例子)。 这些是我设法提出的两个(丑陋)解决方案(不用说,我只是拿起C ++)

1)

class C
{
public:
    class XofC
    {
    public:
        XofC() = delete;
        XofC(C& mom)
        {
            mom.Xlist.emplace_front();
            ref = Xlist.front();
        }
        X& access()
        {
            return ref;
        }
    private:
        X& ref;
    };
    //etc
private:
    std::forward_list<X> Xlist;
    friend class XofC;
    //etc
}

问题:
必须通过XofC实例。

2)

class C
{
public:
    void newX(std::string);
    X& getX(std::string);
    //etc.
private:
    /*possible run-time mapping implementation
    std::vector<X> Xvec;
    std::unordered_map<std::string, decltype(Xvec.size())> NameMap;
    */
    //etc
}

问题:
这样做,但由于X(std::string)的所有名称在编译时都是已知的,因此使用运行时std::unordered_map<std::string, decltype(Xvec.size())>类型的开销会让我觉得这很简单。

可能的()解决方案:使用自动索引(std::string)替换int的编译时。然后我可以使用:

class C
{
public:
    void newX(int); //int: unique index calculated at compile time from std::string
    X& getX(int); //int: unique index calculated at compile time from std::string
//etc.
private:
    std::vector<X> Xvec;
}

问题:

  1. 是否有 3)
  2. 2)可以使用编译时解决方案吗?
  3. 这是现实生活中的情况:我正在开始我的第一个C ++“项目”,我想我可以使用来自一个用户友好,简单快速的参数管理库的实践和实用程序。我打算创建一个ArgMan类,它可以根据一些指定的开关解析argV。程序员将描述性地命名开关并指定触发器字符串(例如,名为 recurse 的开关可以具有"-r""-recursive"作为触发器)。必要时,您应该可以轻松获得开关的设置。实施细节:ArgMan会有std::unordered_map<std::string/*a trigger*/, ??/*something linking to the switch to set on*/>。这确保了argV相对于argC的几乎线性解析。我该怎么做呢?

2 个答案:

答案 0 :(得分:1)

你可以'滥用'非类型模板参数来获取编译时命名实例:

假设我们有一个数据类X

#include <string> 

struct X
{
    int         has_some_properties;
    std::string data;
};

现在,对于我们的命名实例,我们定义了一些名称常量。诀窍是,为了给它们提供外部链接,所以我们可以将地址用作非类型模板参数。

// define some character arrays **with external linkage**
namespace Names
{
    extern const char Vanilla[] = "Vanilla";
    extern const char Banana [] = "Banana";
    extern const char Coconut[] = "Coconut";
    extern const char Shoarma[] = "Shoarma";
}

现在,我们创建一个NamedX包装器,它接受const char*非类型模板参数。包装器包含X静态实例()。

// now we can "adorn" a `namedX` with the name constants (above)
template <const char* Name>
   struct NamedX
{
    static X value;
};

template <const char* Name> X NamedX<Name>::value;

现在您可以像这样使用它:

int main()
{
    X& vanilla = NamedX<Names::Vanilla>::value;

    vanilla = { 42, "Woot!" };

    return vanilla.has_some_properties;
}

请注意,由于模板参数是地址,因此不会进行实际字符串比较。你不能,例如,使用

    X& vanilla = NamedX<"Vanilla">::value;

因为"Vanilla"是没有外部联系的prvalue。所以,实际上你可以做到没有一些复杂性,而是使用标签结构: Live on Coliru

答案 1 :(得分:0)

虽然Neil的解决方案符合我的要求,但在我的图书馆中使用它太噱头了。此外,sehe的技巧肯定是有用的,但是,如果我理解正确,但似乎与我的问题无关。我已经决定使用方法1)模拟所需的行为,这是一个不那么破坏的尝试:

class C
{
private:
    class X
    {
        //std::string member;
        //etc
    };
public:
    class XofC
    {
    public:
        XofC(C & _mom) : mom(_mom)
        {
            mom.Xlist.emplace_front();
            tehX = &(Xlist.front());
        }
        X & get(maybe)
        {
            if (&maybe != &mom) throw std::/*etc*/;
            return &tehX;
        }
    private:
        X * tehX;
        C & mom;
    };
private:
    //etc
    std::forward_list<X> Xlist;
    friend class XofC;
    //etc
};

用法:

C foo;
bar = C::XofC(foo); //acts like an instance of X, but stored in C, but you have to use:
bar.get(foo)/*reference to the actual X*/.member = "_1_";

当然,缺点是你必须确保你在任何你需要的地方通过酒吧,但工作得体。 这就是我的小参数管理器库中的样子: https://raw.github.com/vuplea/arg_manager.h/master/arg_manager.h