Variadic шаблоны и дерево - PullRequest
       22

Variadic шаблоны и дерево

0 голосов
/ 04 ноября 2018

У меня есть следующая команда:

auto result = Builder::get(
        ObjKey("key1"),
        ObjVal("test1"),
        (ObjKey("key2"), ObjVal("test21"), (ObjKey("key3"), ObjVal("test31"), ObjVal("test32")), ObjVal("test22")),
        ObjVal("test2")
    );

Я ожидаю получить следующий результат:

{key1, test1, {key2, test21, {key3, test31, test32}, test22}, test2}

но я получаю такой результат:

{key1, test1, test22, test2}

Код программы ниже (стандарт 11):

#include <iostream>

class ObjKey {
public:
    ObjKey() = delete;

    ObjKey(const std::string& key) : m_key(key) { }

    std::string get() const {
        return m_key + ", ";
    }

private:
    std::string m_key;
};

class ObjVal {
public:
    ObjVal() = delete;

    ObjVal(const std::string& value) : m_value(value) { }

    std::string get() const {
        return m_value + ", ";
    }

private:
    std::string m_value;
};

class Builder {
public:
    template<typename... Args>
    static std::string get(ObjKey&& objKey, Args&& ...args) {
        std::string resultValue;
        get(resultValue, std::forward<ObjKey>(objKey), std::forward<Args>(args)...);
        return resultValue;
    }

private:
    Builder() {}

    template<typename... Args>
    static void get(std::string& resultValue, ObjKey&& objKey, Args&& ...args) {
        resultValue.append("{");
        resultValue.append(objKey.get());
        std::string values;
        get(values, std::forward<Args>(args)...);
        resultValue.append(values);
        resultValue.append("}");
    }

    template<typename... Args>
    static void get(std::string& resultValue, ObjVal&& objVal, Args&& ...args) {
        resultValue.append(objVal.get());
        get(resultValue, std::forward<Args>(args)...);
    }

    static void get(std::string& resultValue) {}
};

int main()
{
    auto result = Builder::get(
        ObjKey("key1"),
        ObjVal("test1"),
        (ObjKey("key2"), ObjVal("test21"), (ObjKey("key3"), ObjVal("test31"), ObjVal("test32")), ObjVal("test22")),
        ObjVal("test2")
    );
    std::cout << result << "\n";
}

Что я делаю не так?

А можно ли решить с помощью шаблонов?

1 Ответ

0 голосов
/ 04 ноября 2018

Что я делаю не так?

Как предполагает n.m., вы должны рассмотреть, что оператор запятой делает со следующим выражением

(ObjKey("key2"), ObjVal("test21"), (ObjKey("key3"), ObjVal("test31"), ObjVal("test32")), ObjVal("test22"))

Оператор запятой отбрасывает все значения, кроме последнего, поэтому выражение становится

(ObjVal("test22"))

А можно ли решить с помощью шаблонов?

Да, но лучшее, что я могу себе представить, это std::tuple.

Я имею в виду ... если вы согласитесь, что get() вызов станет

   auto result = Builder::get(
      ObjKey("key1"),
      ObjVal("test1"),
      std::make_tuple(ObjKey("key2"), ObjVal("test21"),
                      std::make_tuple(ObjKey("key3"), ObjVal("test31"),
                                      ObjVal("test32")),
                      ObjVal("test22")),
      ObjVal("test2"));

поэтому, добавив std::make_tuple перед группировкой (, вы можете добавить пару личных get() для управления регистром кортежей.

Таким образом, переименовав getH() (для «get helper») в частные версии get(), управляющие кортежами версии getH() можно записать следующим образом (решение C ++ 14, поскольку используется * 1030). * и std::make_index_sequence; но если вам нужно решение C ++ 11, не сложно написать заменители C ++ 11)

  template <std::size_t ... Is, typename ... Ts, typename ... As>
  static void getH (std::string & rV, std::index_sequence<Is...> const &,
                    std::tuple<Ts...> const t, As ... as)
   { 
     getH(rV.append(", "), std::get<Is>(t)...);
     getH(rV, as...);
   }

  template <typename ... Ts, typename ... As>
  static void getH (std::string & rV, std::tuple<ObjKey, Ts...> const t,
                    As ... as)
   { getH(rV, std::make_index_sequence<1u+sizeof...(Ts)>{}, t, as...); }

Версии ключа и значения управляются следующим образом

  template <typename ... As>
  static void getH (std::string & rV, ObjKey const & oK, As ... as)
   {
     getH(rV.append(1u, '{').append(oK.get()), as...);
     rV.append(1u, '}');
   }

  template <typename ... As>
  static void getH (std::string & rV, ObjVal const & oV, As ... as)
   { getH(rV.append(", ").append(oV.get()), as...); }

и, очевидно, наземный корпус

  static void getH (std::string &)
   { }

Публичная get() версия стала

  template <typename ... As>
  static std::string get (As ... as)
   {
     std::string resultValue;
     getH(resultValue, as...);
     return resultValue;
   }

Обратите внимание, что я перевел управление запятой с ObjKey и ObjVal на getH() и что я удалил семантику переадресации (вы никогда не использовали std::move).

Ниже приведен полный пример C ++ 14

#include <tuple>
#include <iostream>

class Obj
 {
   public:
      Obj() = delete;

      Obj (std::string const & v0) : v{v0}
       { }

      std::string const & get () const
       { return v; }

   private:
      std::string  v;
 };

struct ObjKey : public Obj
 { ObjKey (std::string const & v0) : Obj{v0} { } };

struct ObjVal : public Obj
 { ObjVal (std::string const & v0) : Obj{v0} { } };

class Builder
 {
   private:
      template <std::size_t ... Is, typename ... Ts, typename ... As>
      static void getH (std::string & rV, std::index_sequence<Is...> const &,
                        std::tuple<Ts...> const t, As ... as)
       { 
         getH(rV.append(", "), std::get<Is>(t)...);
         getH(rV, as...);
       }

      template <typename ... Ts, typename ... As>
      static void getH (std::string & rV, std::tuple<ObjKey, Ts...> const t,
                        As ... as)
       { getH(rV, std::make_index_sequence<1u+sizeof...(Ts)>{}, t, as...); }

      template <typename ... As>
      static void getH (std::string & rV, ObjKey const & oK, As ... as)
       {
         getH(rV.append(1u, '{').append(oK.get()), as...);
         rV.append(1u, '}');
       }

      template <typename ... As>
      static void getH (std::string & rV, ObjVal const & oV, As ... as)
       { getH(rV.append(", ").append(oV.get()), as...); }

      static void getH (std::string &)
       { }

   public:
      template <typename ... As>
      static std::string get (As ... as)
       {
         std::string resultValue;
         getH(resultValue, as...);
         return resultValue;
       }
 };

int main()
 {
   auto result = Builder::get(
      ObjKey("key1"),
      ObjVal("test1"),
      std::make_tuple(ObjKey("key2"), ObjVal("test21"),
                      std::make_tuple(ObjKey("key3"), ObjVal("test31"),
                                      ObjVal("test32")),
                      ObjVal("test22")),
      ObjVal("test2"));

   std::cout << result << "\n";
 }
...