I have a code where I want to display tensor represented as vectors of vectors or std::arrays of std::arrays. The intention is to print them the way numpy prints them. I am still learning meta programming in c and wanted to explore how to print the n-dim container using function template that can take this container of containers and iterate through it recursively and return a string which I can later cout.
Numpy example:
>>> np.ones([2,2])
array([[1., 1.],
[1., 1.]])
>>> np.ones([2,2,4])
array([[[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
>>> np.ones(4)
array([1., 1., 1., 1.])
>>>
Tried so far: I tried the response accepted here as the response:
Print simply STL vectors of vectors recursively in C
It did work for me for 2 dim vectors but the compilation failed for me with 3d vectors when I changed the call to printContainer()
to printContainerV2()
inside printContainerV2()
:
Code:
#include <iostream>
#include <iterator>
#include <vector>
template <typename Iter, typename Cont>
bool isLast(Iter iter, const Cont& cont)
{
return (iter != cont.end()) && (next(iter) == cont.end());
}
template <typename T>
struct is_cont {
static const bool value = false;
};
template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
static const bool value = true;
};
template <typename T>
std::string printContainer(T const& container)
{
std::string str = "{";
for (auto it = std::begin(container); it != std::end(container); it)
if (isLast(it, container))
str = str std::to_string(*it) "}";
else
str = str std::to_string(*it) ",";
return str;
}
template<typename T>
using if_not_cont = std::enable_if<!is_cont<T>::value, T>;
template<typename T>
using if_cont = std::enable_if<is_cont<T>::value, T>;
template <typename T, typename std::enable_if<!is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& container)
{
std::string str = "{";
for (auto it = std::begin(container); it != std::end(container); it)
if (isLast(it, container))
str = str std::to_string(*it) "}";
else
str = str std::to_string(*it) ",";
return str;
}
template <typename T, typename std::enable_if<is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& container)
{
std::string str = "{";
for (auto it = std::begin(container); it != std::end(container); it)
if (isLast(it, container))
str = str printContainerV2(*it) "}";
else
str = str printContainerV2(*it) ",";
return str;
}
int main()
{
std::vector<int> A({2,3,6,8});
std::vector<std::vector<int>> M(2,A);
std::vector<std::vector<std::vector<float>>> m3{{{1,2}, {3,4}},{{5,6}, {7,8}},{{1,2}, {5,9}}};
std::cout << is_cont<decltype(A)>::value << std::endl; // returns true !
// for (auto it = std::begin(M); it != std::end(M); it)
// {
// std::cout << printContainer(*it) << std::endl; // works well std::vector<int>
// std::cout << is_cont<decltype(*it)>::value << std::endl; // return false :(
// }
// Want to use this for printing a std::vector<std::vector<int>>
std::cout << printContainerV2(M) << std::endl; // not working !
std::cout << printContainerV2(m3) << std::endl; // not working
}
Command: clang --std=c 17 test.cpp
test.cpp is the name of the code above.
I got this error:
test.cpp:45:20: error: no matching function for call to 'begin'
for (auto it = std::begin(container); it != std::end(container); it)
^~~~~~~~~~
test.cpp:59:29: note: in instantiation of function template specialization 'printContainerV2<int, nullptr>'
requested here
str = str printContainerV2(*it) "}";
^
test.cpp:59:29: note: in instantiation of function template specialization 'printContainerV2<std::__1::vector<int,
std::__1::allocator<int> >, nullptr>' requested here
test.cpp:80:19: note: in instantiation of function template specialization
'printContainerV2<std::__1::vector<std::__1::vector<int, std::__1::allocator<int> >,
std::__1::allocator<std::__1::vector<int, std::__1::allocator<int> > > >, nullptr>' requested here
std::cout << printContainerV2(M) << std::endl; // not working !
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c /v1/initializer_list:99:1: note:
candidate template ignored: could not match 'initializer_list<type-parameter-0-0>' against 'int'
begin(initializer_list<_Ep> __il) _NOEXCEPT
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c /v1/iterator:1753:1: note:
candidate template ignored: could not match '_Tp [_Np]' against 'const int'
begin(_Tp (&__array)[_Np])
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c /v1/iterator:1771:1: note:
candidate template ignored: substitution failure [with _Cp = const int]: member reference base type
'const int' is not a structure or union
begin(_Cp& __c) -> decltype(__c.begin())
^ ~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c /v1/iterator:1779:1: note:
candidate template ignored: substitution failure [with _Cp = int]: member reference base type 'const int' is
not a structure or union
begin(const _Cp& __c) -> decltype(__c.begin())
^ ~
1 error generated.
CodePudding user response:
Here is a simple way to print vectors of any dimension:
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T> &vec){
std::cout << "[ ";
for(const auto& t: vec){
std::cout << t << " ";
}
std::cout << "] ";
return out;
}
For any other container, you can do the exact same thing.
Use:
int main()
{
std::vector<int> v{1,2,3};
std::cout << v << std::endl;
std::vector<std::vector<std::vector<int>>> v_ds
{
{{{1,2,3},{4,5,6}},{{7,8},{9,10}}, {{1,2,3},{4,5,6}},{{7,8},{9,10}}}
};
std::cout << v_ds << std::endl;
return 0;
}
CodePudding user response:
When your first printContainerV2
is enabled, !is_cont<T>::value
is true
, which means that your T
is no longer a container
type at this time. In your example, they are int
, so you cannot call std::begin
on the int
, you should directly return std::to_string(value)
.
template <typename T, typename std::enable_if<!is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& value)
{
return std::to_string(value);
}