1

I am trying to write a print function for my template class:

struct ColumnKey;

template <class Type, class Key = ColumnKey>
class Column {
protected:
  std::shared_ptr<Type> doGet() {
    std::lock_guard<std::mutex> lock(mutex_);
    return std::make_shared<Type>(value_);
  }
  void doSet(const std::shared_ptr<Type> &value) {
    std::lock_guard<std::mutex> lock(mutex_);
    value_ = *value;
  }
private:
  Type value_;
  std::mutex mutex_;
};

template<class... Columns>
class Table : private Columns... {
public:
  template<class Type, class Key = ColumnKey>
  std::shared_ptr<Type> get() {
    return Column<Type, Key>::doGet();
  }

  template<class Type, class Key = ColumnKey>
  void set(const std::shared_ptr<Type> &value) {
    Column<Type, Key>::doSet(value);
  }

  std::string get_table_row() {
    return "hello_row";
  }
};

I want to create a function get_table_row in class Table, which returns columnA + "," + columnB + "," + ..

I am trying to write in this way, but getting compilation errors. Can somebody point the mistake in my approach?

    template <class Column<class Type, class Key = ColumnKey>>
    std::string get_row() {
        return std::to_string( *Column<Type, Key>::doGet() );
    }

    template <class Column<class Type, class Key = ColumnKey>, class... Columns>
    std::string get_row() {
        return ( std::to_string(*Column<Type, Key>::doGet()) + "," + Columns.get_row() );
    }

I am struggling to do that, can anybody guide me ?

user17836
  • 103
  • 1
  • 7
  • You may want to iterate over the template arguments recursively, see [this answer](https://stackoverflow.com/a/12342686/948128) – piwi Nov 28 '17 at 09:08
  • Possible duplicate of [How do I print out the arguments of a function using a variadic template?](https://stackoverflow.com/questions/12342633/how-do-i-print-out-the-arguments-of-a-function-using-a-variadic-template) – piwi Nov 28 '17 at 09:09
  • @piwi I think that is a little different that what I want to achieve. I have also `Key`, and I want to `return` a value. Also, I only want to call `Table, Column, Column>.get_row()`. Basically, without passing any arguments. – user17836 Nov 28 '17 at 09:18
  • @piwi may be it is linked, but I am confused, any help would be appreciated – user17836 Nov 28 '17 at 09:19
  • @piwi can you please have a look at my attempt at the solution? What is wrong? I get compilation errors. – user17836 Nov 28 '17 at 09:27
  • I am looking into it. – piwi Nov 28 '17 at 09:29
  • 1
    Your use of `shared_ptr` seems very convoluted to me. What are you trying to do there? In the current implementation, each call to `doGet` ends up copying the column content into a newly allocated `shared_ptr` – Rerito Nov 28 '17 at 09:44
  • @Rerito I can remove the `shared_ptr` and simply use `Type&`. But how do I solve what I am trying to do? I now am testing another version, without `shared_ptr`. – user17836 Nov 28 '17 at 09:49
  • You should provide a [mcve]. Specifically, all the clutter about `std::shared_ptr` and `std::mutex` should be removed as it has nothing to do with the question – Passer By Nov 28 '17 at 10:13
  • @PasserBy: I will keep that in mind from next time :) – user17836 Nov 28 '17 at 11:09

3 Answers3

0

Here is a solution that may work.

The struct printer iterates over the variadic template and invoke Table::get for each type:

template <typename TableT>
struct printer
{
  printer(TableT& t): m_table(t) {}

  template <typename T>
  void impl(std::ostream& output)
  {
    using type = typename column_type<T>::type;
    output << *m_table.template get<type>();
  }

  template <typename First, typename Second, typename... Other>
  void impl(std::ostream& output)
  {
    impl<First>(output);
    impl<Second, Other...>(output);
  }

  template <typename... T>
  std::string invoke()
  {
    std::ostringstream out;
    impl<T...>(out);
    return out.str();
  }

  TableT& m_table;
};

I wrote a trait to deduce the column Type:

template <typename> struct column_type;
template <template <typename, typename> class Column, typename Type, typename Key>
struct column_type<Column<Type, Key>>
{
    using type = Type;
};

In order to use printer, I saved the Columns types in a tuple and unpack it:

template <typename... Columns>
class Table: private Columns...
{
  using columns_t = std::tuple<Columns...>;
public:
    std::string get_table_row() {
    return unpack<columns_t>::invoke(*this);
  }
};

The struct unpack invokes the printer:

template <typename> struct unpack;
template <typename... T> struct unpack<std::tuple<T...>>
{
  template <typename TableT>
  static std::string invoke(TableT& t)
  {
    printer<TableT> p(t);
    return p.invoke<T...>();
  }
};
piwi
  • 5,136
  • 2
  • 24
  • 48
0

Your get_row member functions are indeed ill-formed. We will need to process the columns recursively. To do so we will need an helper class (because function templates cannot be partially specialized), along with a helper type traits to deduce your Column's Key and Type parameters:

template <typename>
struct column_traits;

template <typename Type, typename Key>
struct column_traits<Column<Type, Key>> {
    using type = Type;
    using key = Key;
};

template <typename...>
struct TableRowPrinter;

template <typename Col>
struct TableRowPrinter<Col> {
    template <typename TableT>
    static std::string get_row(TableT& table) {
        using col_traits = column_traits<Col>;
        return std::to_string(
               *table.template get<typename col_traits::type,
                                   typename col_traits::key>());
    }
};

template <typename HeadCol, typename... Cols>
struct TableRowPrinter<HeadCol, Cols...> {
    template <typename TableT>
    static std::string get_row(TableT& table) {
        using col_traits = column_traits<HeadCol>;
        return std::to_string(
                   *table.template get<typename col_traits::type,
                                       typename col_traits::key>()) +
               ", " + TableRowPrinter<Cols...>::get_row(table);
    }
};

Now, in the Table class template, all you need to do is to add the proper member function:

std::string get_row() {
    return TableRowPrinter<Columns...>::get_row(*this);
}

You can see a working example on Coliru.

Note: I didn't "clean" the unusual shared_ptr usage... That is another topic...

Rerito
  • 5,886
  • 21
  • 47
0

You should use fold expressions

template<typename T, typename K>
struct Column
{
    T val;
};

template<typename FirstColumn, typename... Columns>
struct Table : private FirstColumn, private Columns...
{
    std::string get_table_row()
    {
        using std::to_string;
        return (to_string(FirstColumn::val) + ... + ("," + to_string(Columns::val)));
    }
};

You could specialize for the case where there is zero columns if you have a use case for that.

Passer By
  • 19,325
  • 6
  • 49
  • 96