@ -1,9 +1,13 @@
// TODO: Replace `cout, printf` with sprintf&fputs and custom buffers
# ifndef _TABLE_H
# ifndef _TABLE_H
# define _TABLE_H
# define _TABLE_H
# include "types.h"
# include "types.h"
# include "vector_type.hpp"
# include "vector_type.hpp"
# include <iostream>
# include <iostream>
# include <string>
# include "io.h"
template < typename T >
template < typename T >
class vector_type ;
class vector_type ;
template < >
template < >
@ -24,8 +28,10 @@ template<typename _Ty>
class ColRef : public vector_type < _Ty >
class ColRef : public vector_type < _Ty >
{
{
public :
public :
typedef ColRef < _Ty > Decayed_t ;
const char * name ;
const char * name ;
types : : Type_t ty = types : : ERROR ;
types : : Type_t ty = types : : ERROR ;
ColRef ( ) : vector_type < _Ty > ( 0 ) , name ( " " ) { }
ColRef ( const uint32_t & size , const char * name = " " ) : vector_type < _Ty > ( size ) , name ( name ) { }
ColRef ( const uint32_t & size , const char * name = " " ) : vector_type < _Ty > ( size ) , name ( name ) { }
ColRef ( const char * name ) : name ( name ) { }
ColRef ( const char * name ) : name ( name ) { }
void init ( ) { ty = types : : Types < _Ty > : : getType ( ) ; this - > size = this - > capacity = 0 ; this - > container = 0 ; }
void init ( ) { ty = types : : Types < _Ty > : : getType ( ) ; this - > size = this - > capacity = 0 ; this - > container = 0 ; }
@ -35,6 +41,14 @@ public:
ColView < _Ty > operator [ ] ( const vector_type < uint32_t > & idxs ) const {
ColView < _Ty > operator [ ] ( const vector_type < uint32_t > & idxs ) const {
return ColView < _Ty > ( * this , idxs ) ;
return ColView < _Ty > ( * this , idxs ) ;
}
}
void out ( uint32_t n = 4 , const char * sep = " " ) const {
n = n > this - > size ? this - > size : n ;
std : : cout < < ' ( ' ;
for ( uint32_t i = 0 ; i < n ; + + i )
std : : cout < < this - > operator [ ] ( i ) < < sep ;
std : : cout < < ' ) ' ;
}
template < typename T >
template < typename T >
ColRef < T > scast ( ) ;
ColRef < T > scast ( ) ;
} ;
} ;
@ -42,6 +56,7 @@ public:
template < typename _Ty >
template < typename _Ty >
class ColView {
class ColView {
public :
public :
typedef ColRef < _Ty > Decayed_t ;
const vector_type < uint32_t > & idxs ;
const vector_type < uint32_t > & idxs ;
const ColRef < _Ty > & orig ;
const ColRef < _Ty > & orig ;
const uint32_t & size ;
const uint32_t & size ;
@ -75,8 +90,22 @@ public:
Iterator_t end ( ) const {
Iterator_t end ( ) const {
return Iterator_t ( idxs . end ( ) , orig ) ;
return Iterator_t ( idxs . end ( ) , orig ) ;
}
}
void out ( uint32_t n = 4 , const char * sep = " " ) const {
n = n > size ? size : n ;
std : : cout < < ' ( ' ;
for ( uint32_t i = 0 ; i < n ; + + i )
std : : cout < < this - > operator [ ] ( i ) < < sep ;
std : : cout < < ' ) ' ;
}
} ;
} ;
template < template < class . . . > class VT , class T >
std : : ostream & operator < < ( std : : ostream & os , const VT < T > & v )
{
v . out ( ) ;
return os ;
}
template < class Type >
struct decayed_impl < ColView , Type > { typedef ColRef < Type > type ; } ;
template < typename _Ty >
template < typename _Ty >
template < typename T >
template < typename T >
inline ColRef < T > ColRef < _Ty > : : scast ( )
inline ColRef < T > ColRef < _Ty > : : scast ( )
@ -85,13 +114,62 @@ inline ColRef<T> ColRef<_Ty>::scast()
return * ( ColRef < T > * ) this ;
return * ( ColRef < T > * ) this ;
}
}
using uColRef = ColRef < void > ;
using uColRef = ColRef < void > ;
template < class . . . Types > struct TableInfo ;
template < class . . . Types > struct TableView ;
template < long long _Index , bool order = true , class . . . _Types >
constexpr inline auto & get ( const TableInfo < _Types . . . > & table ) noexcept {
if constexpr ( order )
return * ( ColRef < std : : tuple_element_t < _Index , std : : tuple < _Types . . . > > > * ) & ( table . colrefs [ _Index ] ) ;
else
return * ( ColRef < std : : tuple_element_t < - 1 - _Index , std : : tuple < _Types . . . > > > * ) & ( table . colrefs [ - 1 - _Index ] ) ;
}
template < long long _Index , class . . . _Types >
constexpr inline ColRef < std : : tuple_element_t < _Index , std : : tuple < _Types . . . > > > & get ( const TableView < _Types . . . > & table ) noexcept {
return * ( ColRef < std : : tuple_element_t < _Index , std : : tuple < _Types . . . > > > * ) & ( table . info . colrefs [ _Index ] ) ;
}
template < class V >
struct is_vector_impl < ColRef < V > > : std : : true_type { } ;
template < class V >
struct is_vector_impl < ColView < V > > : std : : true_type { } ;
template < class V >
struct is_vector_impl < vector_type < V > > : std : : true_type { } ;
template < class . . . Types >
struct TableView ;
template < class . . . Types >
template < class . . . Types >
struct TableInfo {
struct TableInfo {
const char * name ;
const char * name ;
ColRef < void > * colrefs ;
ColRef < void > * colrefs ;
uint32_t n_cols ;
uint32_t n_cols ;
void print ( const char * __restrict sep , const char * __restrict end ) const ;
typedef std : : tuple < Types . . . > tuple_type ;
typedef std : : tuple < Types . . . > tuple_type ;
void print ( const char * __restrict sep , const char * __restrict end ) const ;
template < class . . . Types2 >
struct lineage_t {
TableInfo < Types . . . > * this_table ;
TableInfo < Types2 . . . > * table ;
vector_type < uint32_t > rid ;
constexpr lineage_t ( TableInfo < Types . . . > * this_table , TableInfo < Types2 . . . > * table )
: this_table ( this_table ) , table ( table ) , rid ( 0 ) { }
constexpr lineage_t ( ) : this_table ( 0 ) , table ( 0 ) , rid ( 0 ) { }
template < int col >
inline auto & get ( uint32_t idx ) {
return get < col > ( * table ) [ rid [ idx ] ] ;
}
void emplace_back ( const uint32_t & v ) {
rid . emplace_back ( v ) ;
}
} ;
template < class . . . Types2 >
auto bind ( TableInfo < Types2 . . . > * table2 ) {
return lineage_t ( this , table2 ) ;
}
template < size_t j = 0 >
template < size_t j = 0 >
typename std : : enable_if < j = = sizeof . . . ( Types ) - 1 , void > : : type print_impl ( const uint32_t & i , const char * __restrict sep = " " ) const ;
typename std : : enable_if < j = = sizeof . . . ( Types ) - 1 , void > : : type print_impl ( const uint32_t & i , const char * __restrict sep = " " ) const ;
template < size_t j = 0 >
template < size_t j = 0 >
@ -103,12 +181,106 @@ struct TableInfo {
template < size_t . . . Idxs >
template < size_t . . . Idxs >
using getRecordType = typename GetTypes < Idxs . . . > : : type ;
using getRecordType = typename GetTypes < Idxs . . . > : : type ;
TableInfo ( const char * name , uint32_t n_cols ) ;
TableInfo ( const char * name , uint32_t n_cols ) ;
template < int prog = 0 >
inline void materialize ( const vector_type < uint32_t > & idxs , TableInfo < Types . . . > * tbl = nullptr ) { // inplace materialize
if constexpr ( prog = = 0 ) tbl = ( tbl = = 0 ? this : tbl ) ;
if constexpr ( prog = = sizeof . . . ( Types ) ) return ;
else {
auto & col = get < prog > ( * this ) ;
auto new_col = decays < decltype ( col ) > { idxs . size } ;
for ( uint32_t i = 0 ; i < idxs . size ; + + i )
new_col [ i ] = col [ idxs [ i ] ] ;
get < prog > ( * tbl ) = new_col ;
materialize < prog + 1 > ( idxs , tbl ) ;
}
}
inline TableInfo < Types . . . > * materialize_copy ( const vector_type < uint32_t > & idxs ) {
auto tbl = new TableInfo < Types . . . > ( this - > name , sizeof . . . ( Types ) ) ;
materialize < 0 > ( idxs , tbl ) ;
return tbl ;
}
template < int . . . cols >
inline vector_type < uint32_t > * order_by ( vector_type < uint32_t > * ord = nullptr ) {
if ( ! ord ) {
ord = new vector_type < uint32_t > ( colrefs [ 0 ] . size ) ;
for ( uint32_t i = 0 ; i < colrefs [ 0 ] . size ; + + i )
( * ord ) [ i ] = i ;
}
std : : sort ( ord - > begin ( ) , ord - > end ( ) , [ this ] ( const uint32_t & lhs , const uint32_t & rhs ) {
return
std : : forward_as_tuple ( ( cols > = 0 ? get < cols , ( cols > = 0 ) > ( * this ) [ lhs ] : - get < cols , ( cols > = 0 ) > ( * this ) [ lhs ] ) . . . )
<
std : : forward_as_tuple ( ( cols > = 0 ? get < cols , ( cols > = 0 ) > ( * this ) [ rhs ] : - get < cols , ( cols > = 0 ) > ( * this ) [ rhs ] ) . . . ) ;
} ) ;
return ord ;
}
template < int . . . cols >
auto order_by_view ( ) {
return TableView < Types . . . > ( order_by < cols . . . > ( ) , * this ) ;
}
// Print 2 -- generate printf string first, supports flattening, supports sprintf/printf/fprintf
template < int col , int . . . rem_cols , class Fn , class . . . __Types >
inline void print2_impl ( Fn func , const uint32_t & i , const __Types & . . . args ) const {
using this_type = typename std : : tuple_element < col , tuple_type > : : type ;
const auto & this_value = get < col > ( * this ) [ i ] ;
const auto & next = [ & ] ( auto & v ) {
if constexpr ( sizeof . . . ( rem_cols ) = = 0 )
func ( args . . . , v ) ;
else
print2_impl < rem_cols . . . > ( func , i , args . . . , v ) ;
} ;
if constexpr ( is_vector_type < this_type > )
for ( int j = 0 ; j < this_value . size ; + + j )
next ( this_value [ j ] ) ;
else
next ( this_value ) ;
}
template < int . . . cols >
void print2 ( const char * __restrict sep = " , " , const char * __restrict end = " \n " ,
const vector_type < uint32_t > * __restrict view = nullptr , FILE * __restrict fp = nullptr ) const {
std : : string printf_string =
generate_printf_string < typename std : : tuple_element < cols , tuple_type > : : type . . . > ( sep , end ) ;
const auto & prt_loop = [ & fp , & view , & printf_string , * this ] ( const auto & f ) {
if ( view )
for ( int i = 0 ; i < view - > size ; + + i )
print2_impl < cols . . . > ( f , ( * view ) [ i ] , printf_string . c_str ( ) ) ;
else
for ( int i = 0 ; i < colrefs [ 0 ] . size ; + + i )
print2_impl < cols . . . > ( f , i , printf_string . c_str ( ) ) ;
} ;
if ( fp )
prt_loop ( [ & fp ] ( auto . . . args ) { fprintf ( fp , args . . . ) ; } ) ;
else
prt_loop ( printf ) ;
}
template < int . . . vals > struct applier {
inline constexpr static void apply ( const TableInfo < Types . . . > & t , const char * __restrict sep = " , " , const char * __restrict end = " \n " ,
const vector_type < uint32_t > * __restrict view = nullptr , FILE * __restrict fp = nullptr )
{ t . template print2 < vals . . . > ( sep , end , view , fp ) ; } } ;
inline void printall ( const char * __restrict sep = " , " , const char * __restrict end = " \n " ,
const vector_type < uint32_t > * __restrict view = nullptr , FILE * __restrict fp = nullptr ) {
applyIntegerSequence < sizeof . . . ( Types ) , applier > : : apply ( * this , sep , end , view , fp ) ;
}
} ;
} ;
template < size_t _Index , class . . . _Types >
constexpr inline ColRef < std : : tuple_element_t < _Index , std : : tuple < _Types . . . > > > & get ( const TableInfo < _Types . . . > & table ) noexcept {
template < class . . . Types >
return * ( ColRef < std : : tuple_element_t < _Index , std : : tuple < _Types . . . > > > * ) & ( table . colrefs [ _Index ] ) ;
struct TableView {
}
const vector_type < uint32_t > * idxs ;
const TableInfo < Types . . . > & info ;
constexpr TableView ( const vector_type < uint32_t > * idxs , const TableInfo < Types . . . > & info ) noexcept : idxs ( idxs ) , info ( info ) { }
void print ( const char * __restrict sep , const char * __restrict end ) const ;
template < size_t j = 0 >
typename std : : enable_if < j = = sizeof . . . ( Types ) - 1 , void > : : type print_impl ( const uint32_t & i , const char * __restrict sep = " " ) const ;
template < size_t j = 0 >
typename std : : enable_if < j < sizeof . . . ( Types ) - 1 , void > : : type print_impl ( const uint32_t & i , const char * __restrict sep = " " ) const ;
~ TableView ( ) {
delete idxs ;
}
} ;
template < class T >
template < class T >
constexpr static inline bool is_vector ( const ColRef < T > & ) {
constexpr static inline bool is_vector ( const ColRef < T > & ) {
@ -118,15 +290,37 @@ template <class T>
constexpr static inline bool is_vector ( const vector_type < T > & ) {
constexpr static inline bool is_vector ( const vector_type < T > & ) {
return true ;
return true ;
}
}
template < class T >
constexpr static inline bool is_vector ( const T & ) {
return false ;
}
template < class . . . Types >
template < class . . . Types >
TableInfo < Types . . . > : : TableInfo ( const char * name , uint32_t n_cols ) : name ( name ) , n_cols ( n_cols ) {
TableInfo < Types . . . > : : TableInfo ( const char * name , uint32_t n_cols ) : name ( name ) , n_cols ( n_cols ) {
this - > colrefs = ( ColRef < void > * ) malloc ( sizeof ( ColRef < void > ) * n_cols ) ;
this - > colrefs = ( ColRef < void > * ) malloc ( sizeof ( ColRef < void > ) * n_cols ) ;
}
}
template < class . . . Types >
template < size_t j >
inline typename std : : enable_if < j = = sizeof . . . ( Types ) - 1 , void > : : type
TableView < Types . . . > : : print_impl ( const uint32_t & i , const char * __restrict sep ) const {
std : : cout < < ( get < j > ( * this ) ) [ ( * idxs ) [ i ] ] ;
}
template < class . . . Types >
template < size_t j >
inline typename std : : enable_if < j < sizeof . . . ( Types ) - 1 , void > : : type
TableView < Types . . . > : : print_impl ( const uint32_t & i , const char * __restrict sep ) const
{
std : : cout < < ( get < j > ( * this ) ) [ ( * idxs ) [ i ] ] < < sep ;
print_impl < j + 1 > ( i , sep ) ;
}
template < class . . . Types >
inline void TableView < Types . . . > : : print ( const char * __restrict sep , const char * __restrict end ) const {
int n_rows = 0 ;
if ( info . colrefs [ 0 ] . size > 0 )
n_rows = info . colrefs [ 0 ] . size ;
for ( int i = 0 ; i < n_rows ; + + i ) {
print_impl ( i ) ;
std : : cout < < end ;
}
}
template < class . . . Types >
template < class . . . Types >
template < size_t j >
template < size_t j >
inline typename std : : enable_if < j = = sizeof . . . ( Types ) - 1 , void > : : type
inline typename std : : enable_if < j = = sizeof . . . ( Types ) - 1 , void > : : type
@ -153,60 +347,60 @@ inline void TableInfo<Types...>::print(const char* __restrict sep, const char* _
std : : cout < < end ;
std : : cout < < end ;
}
}
}
}
template < class T1 , class T2 , template < typename . . . > class VT >
template < class T1 , class T2 , template < typename . . . > class VT , template < typename . . . > class VT2 >
VT< typename types : : Coercion < T1 , T2 > : : type > operator - ( const VT < T1 > & lhs , const VT < T2 > & rhs ) {
decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > operator - ( const VT < T1 > & lhs , const VT 2 < T2 > & rhs ) {
auto ret = VT< typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
auto ret = decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
for ( int i = 0 ; i < lhs . size ; + + i )
for ( int i = 0 ; i < lhs . size ; + + i )
ret .container [i ] = lhs .container [i ] - rhs . container [ i ] ;
ret [i ] = lhs [i ] - rhs [ i ] ;
return ret ;
return ret ;
}
}
template < class T1 , class T2 , template < typename . . . > class VT >
template < class T1 , class T2 , template < typename . . . > class VT >
VT< typename types : : Coercion < T1 , T2 > : : type > operator - ( const VT < T1 > & lhs , const T2 & rhs ) {
decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > operator - ( const VT < T1 > & lhs , const T2 & rhs ) {
auto ret = VT< typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
auto ret = decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
for ( int i = 0 ; i < lhs . size ; + + i )
for ( int i = 0 ; i < lhs . size ; + + i )
ret .container [i ] = lhs . container [ i ] - rhs ;
ret [i ] = lhs [ i ] - rhs ;
return ret ;
return ret ;
}
}
template < class T1 , class T2 , template < typename . . . > class VT >
template < class T1 , class T2 , template < typename . . . > class VT , template < typename . . . > class VT2 >
VT< typename types : : Coercion < T1 , T2 > : : type > operator + ( const VT < T1 > & lhs , const VT < T2 > & rhs ) {
decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > operator + ( const VT < T1 > & lhs , const VT 2 < T2 > & rhs ) {
auto ret = VT< typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
auto ret = decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
for ( int i = 0 ; i < lhs . size ; + + i )
for ( int i = 0 ; i < lhs . size ; + + i )
ret .container [i ] = lhs .container [i ] + rhs . container [ i ] ;
ret [i ] = lhs [i ] + rhs [ i ] ;
return ret ;
return ret ;
}
}
template < class T1 , class T2 , template < typename . . . > class VT >
template < class T1 , class T2 , template < typename . . . > class VT >
VT< typename types : : Coercion < T1 , T2 > : : type > operator + ( const VT < T1 > & lhs , const T2 & rhs ) {
decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > operator + ( const VT < T1 > & lhs , const T2 & rhs ) {
auto ret = VT< typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
auto ret = decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
for ( int i = 0 ; i < lhs . size ; + + i )
for ( int i = 0 ; i < lhs . size ; + + i )
ret .container [i ] = lhs . container [ i ] + rhs ;
ret [i ] = lhs [ i ] + rhs ;
return ret ;
return ret ;
}
}
template < class T1 , class T2 , template < typename . . . > class VT >
template < class T1 , class T2 , template < typename . . . > class VT , template < typename . . . > class VT2 >
VT< typename types : : Coercion < T1 , T2 > : : type > operator * ( const VT < T1 > & lhs , const VT < T2 > & rhs ) {
decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > operator * ( const VT < T1 > & lhs , const VT 2 < T2 > & rhs ) {
auto ret = VT< typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
auto ret = decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
for ( int i = 0 ; i < lhs . size ; + + i )
for ( int i = 0 ; i < lhs . size ; + + i )
ret .container [i ] = lhs .container [i ] * rhs . container [ i ] ;
ret [i ] = lhs [i ] * rhs [ i ] ;
return ret ;
return ret ;
}
}
template < class T1 , class T2 , template < typename . . . > class VT >
template < class T1 , class T2 , template < typename . . . > class VT >
VT< typename types : : Coercion < T1 , T2 > : : type > operator * ( const VT < T1 > & lhs , const T2 & rhs ) {
decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > operator * ( const VT < T1 > & lhs , const T2 & rhs ) {
auto ret = VT< typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
auto ret = decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
for ( int i = 0 ; i < lhs . size ; + + i )
for ( int i = 0 ; i < lhs . size ; + + i )
ret .container [i ] = lhs . container [ i ] * rhs ;
ret [i ] = lhs [ i ] * rhs ;
return ret ;
return ret ;
}
}
template < class T1 , class T2 , template < typename . . . > class VT >
template < class T1 , class T2 , template < typename . . . > class VT , template < typename . . . > class VT2 >
VT< typename types : : Coercion < T1 , T2 > : : type > operator / ( const VT < T1 > & lhs , const VT < T2 > & rhs ) {
decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > operator / ( const VT < T1 > & lhs , const VT 2 < T2 > & rhs ) {
auto ret = VT< typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
auto ret = decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
for ( int i = 0 ; i < lhs . size ; + + i )
for ( int i = 0 ; i < lhs . size ; + + i )
ret .container [i ] = lhs .container [i ] / rhs . container [ i ] ;
ret [i ] = lhs [i ] / rhs [ i ] ;
return ret ;
return ret ;
}
}
template < class T1 , class T2 , template < typename . . . > class VT >
template < class T1 , class T2 , template < typename . . . > class VT >
VT< typename types : : Coercion < T1 , T2 > : : type > operator / ( const VT < T1 > & lhs , const T2 & rhs ) {
decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > operator / ( const VT < T1 > & lhs , const T2 & rhs ) {
auto ret = VT< typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
auto ret = decayed_t< VT , typename types : : Coercion < T1 , T2 > : : type > ( lhs . size , " " ) ;
for ( int i = 0 ; i < lhs . size ; + + i )
for ( int i = 0 ; i < lhs . size ; + + i )
ret .container [i ] = lhs . container [ i ] / rhs ;
ret [i ] = lhs [ i ] / rhs ;
return ret ;
return ret ;
}
}
@ -214,17 +408,24 @@ template <class ...Types>
void print ( const TableInfo < Types . . . > & v , const char * delimiter = " " , const char * endline = " \n " ) {
void print ( const TableInfo < Types . . . > & v , const char * delimiter = " " , const char * endline = " \n " ) {
v . print ( delimiter , endline ) ;
v . print ( delimiter , endline ) ;
}
}
template < class . . . Types >
void print ( const TableView < Types . . . > & v , const char * delimiter = " " , const char * endline = " \n " ) {
v . print ( delimiter , endline ) ;
}
template < class T >
template < class T >
void print ( const T & v , const char * delimiter = " " ) {
void print ( const T & v , const char * delimiter = " " ) {
printf ( types : : printf_str [ types : : Types < T > : : getType ( ) ] , v ) ;
std : : cout < < v ;
// printf(types::printf_str[types::Types<T>::getType()], v);
}
}
template < class T >
template < class T >
void inline print_impl ( const T & v , const char * delimiter , const char * endline ) {
void inline print_impl ( const T & v , const char * delimiter , const char * endline ) {
for ( const auto & vi : v ) {
for ( const auto & vi : v ) {
print ( vi ) ;
print ( vi ) ;
printf ( " %s " , delimiter ) ;
std : : cout < < delimiter ;
// printf("%s", delimiter);
}
}
printf ( " %s " , endline ) ;
std : : cout < < endline ;
//printf("%s", endline);
}
}
template < class T , template < typename > class VT >
template < class T , template < typename > class VT >