I'm trying to implement a simple N-dimensional array. This seems to be working more or less properly but I just can't get its overloaded ostream operator work. Here's my current implementation:
#include <iostream>
#include <memory>
#include <vector>
template <typename Type, int Dimension>
struct Array {
typedef std::vector<typename Array<Type, Dimension - 1>::type> type;
template <typename cType, int cDimension>
friend std::ostream &operator<<(std::ostream &stream, const Array<cType, cDimension>::type &object) {
if (cDimension == 0) {
stream << object << ' ';
} else {
typedef typename Array<cType, cDimension>::type::iterator ArrayIterator;
for (ArrayIterator it = object.begin(); it != object.end(); ++it) {
typedef typename Array<cType, cDimension - 1>::type NestedArray;
NestedArray nArray = (NestedArray)(*it);
stream << nArray << std::endl;
}
}
return stream;
}
};
template <typename Type>
struct Array < Type, 0 > {
typedef Type type;
};
int main() {
Array<int, 1>::type c00 = { 1, 2, 3 };
Array<int, 1>::type c01 = { 2, 3, 4 };
Array<int, 1>::type c02 = { 3, 4, 5 };
Array<int, 2>::type c10 = { c00, c01 };
Array<int, 2>::type c11 = { c01, c02 };
Array<int, 3>::type c20 = { c10, c11 };
std::cout << c20 << std::endl;
return 0;
}
I'm getting the following compilation errors:
1>------ Build started: Project: NDepthArray, Configuration: Debug Win32 ------
1> Source.cpp
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): warning C4346: 'Array<Type,Dimension>::type' : dependent name is not a type
1> prefix with 'typename' to indicate a type
1> c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(25) : see reference to class template instantiation 'Array<Type,Dimension>' being compiled
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): error C2061: syntax error : identifier 'type'
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): error C2805: binary 'operator <<' has too few parameters
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): fatal error C1903: unable to recover from previous error(s); stopping compilation
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
I literally tried all my ideas already, which includes removing friend keyword and the actual class parameter but nothing changes. How can we overload operators for such class templates?
Cheers,
Joey
The problem with your approach is that the cType cannot be inferred:
template <typename Type, int Dimension> // Asking for Type as cType
struct Array {
typedef std::vector<typename Array<Type, Dimension - 1>::type> type;
}
template <typename cType, int cDimension> ↓↓↓↓↓
std::ostream &operator<<(std::ostream &stream, typename Array<cType, cDimension>::type &object)
Array<int, 3>::type c20 = { c10, c11 };
std::cout << c20 // Deduction failure
You can find more info here: https://stackoverflow.com/a/12566268/1938163
14.8.2.5/4
In certain contexts, however, the value does not participate in type
deduction, but instead uses the values of template arguments that were
either deduced elsewhere or explicitly specified. If a template
parameter is used only in non-deduced contexts and is not explicitly
specified, template argument deduction fails.
As a sidenote: implementing complex structures for multidimensional arrays or vectors with templated recursive code is a not-very-maintainable and surely hard-to-read path to achieve something that could have been done faster, more efficient (less allocations) and clearer with only a contiguous block of memory indexed in different strides.
Related
Building on top of this example:
https://www.boost.org/doc/libs/1_75_0/libs/geometry/doc/html/geometry/spatial_indexes/rtree_examples/using_indexablegetter_function_object___storing_indexes_of_external_container_s_elements.html
I have constructed the following MWE to build and query spatial trees based on indices onto non-trivial containers. The example this was based upon assumes that Container::value_type is already a leaf of the tree. I simply adapted the example to work with any indexable type, i.e., any type understood by bgi::indexable<typename Container::value_type>.
The included MWE works just fine on Linux and Mac, but fails to compile on Windows, and I'm struggling in understanding what the problem may be. A working example is here https://godbolt.org/z/vTT5r5MWc, where you can see that if you switch to gcc or clang, everything works, but with MSVC19 we get the errors reported below.
Any idea on how to modify the IndexableGetter/anything else to make this work under MSVC?
#include <boost/geometry/index/rtree.hpp>
#include <boost/geometry/strategies/strategies.hpp>
#include <boost/range/irange.hpp>
#include <iostream>
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
template <typename LeafType,
typename IndexType = bgi::linear<16>,
typename IndexableGetter = bgi::indexable<LeafType>>
using RTree = bgi::rtree<LeafType, IndexType, IndexableGetter>;
using Point = bg::model::point<double, 2, bg::cs::cartesian>;
template <typename Container>
class IndexableGetterFromIndices
{
public:
using IndexableGetter =
typename bgi::indexable<typename Container::value_type>;
using result_type = typename IndexableGetter::result_type;
using size_t = typename Container::size_type;
explicit IndexableGetterFromIndices(Container const &c)
: container(c)
{}
result_type
operator()(size_t i) const
{
return getter(container[i]);
}
private:
const Container &container;
IndexableGetter getter;
};
template <typename IndexType = boost::geometry::index::linear<16>,
typename ContainerType>
RTree<typename ContainerType::size_type,
IndexType,
IndexableGetterFromIndices<ContainerType>>
pack_rtree_of_indices(const ContainerType &container)
{
boost::integer_range<typename ContainerType::size_type> indices(
0, container.size());
return RTree<typename ContainerType::size_type,
IndexType,
IndexableGetterFromIndices<ContainerType>>(
indices.begin(),
indices.end(),
IndexType(),
IndexableGetterFromIndices<ContainerType>(container));
}
int
main()
{
std::vector<std::pair<Point, int>> points;
// create some points
for (unsigned i = 0; i < 10; ++i)
points.push_back(std::make_pair(Point(i + 0.0, i + 0.0), i * 10));
const auto tree = pack_rtree_of_indices(points);
for (const auto result :
tree | bgi::adaptors::queried(bgi::nearest(Point(3.0, 4.0), 1)))
{
std::cout << "Nearest point: " << bg::wkt<Point>(points[result].first)
<< ", index = " << points[result].second << std::endl;
}
}
The error I get on Windows is
example.cpp
C:/data/libraries/installed/x64-windows/include\boost/geometry/index/rtree.hpp(1762): error C2664: 'boost::geometry::index::detail::translator<IndexableGetter,EqualTo>::translator(const boost::geometry::index::indexable<std::pair<Point,int>> &,const EqualTo &)': cannot convert argument 1 from 'const IndGet' to 'const boost::geometry::index::indexable<std::pair<Point,int>> &'
with
[
IndexableGetter=IndexableGetterFromIndices<std::vector<std::pair<Point,int>,std::allocator<std::pair<Point,int>>>>,
EqualTo=boost::geometry::index::equal_to<std::_Default_allocator_traits<std::allocator<std::pair<Point,int>>>::size_type>
]
and
[
IndGet=IndexableGetterFromIndices<std::vector<std::pair<Point,int>,std::allocator<std::pair<Point,int>>>>
]
C:/data/libraries/installed/x64-windows/include\boost/geometry/index/rtree.hpp(1768): note: Reason: cannot convert from 'const IndGet' to 'const boost::geometry::index::indexable<std::pair<Point,int>>'
with
[
IndGet=IndexableGetterFromIndices<std::vector<std::pair<Point,int>,std::allocator<std::pair<Point,int>>>>
]
C:/data/libraries/installed/x64-windows/include\boost/geometry/index/rtree.hpp(1762): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
C:/data/libraries/installed/x64-windows/include\boost/geometry/index/detail/translator.hpp(51): note: see declaration of 'boost::geometry::index::detail::translator<IndexableGetter,EqualTo>::translator'
with
[
IndexableGetter=IndexableGetterFromIndices<std::vector<std::pair<Point,int>,std::allocator<std::pair<Point,int>>>>,
EqualTo=boost::geometry::index::equal_to<std::_Default_allocator_traits<std::allocator<std::pair<Point,int>>>::size_type>
]
....
Thanks for a very neat self-contained example.
It does compile with /std::latest:
x86 msvc v19.latest, Live On Compiler Explorer).
x64 msvc v19.latest, Live On Compiler Explorer).
In retrospect there was the clue:
cl : Command line warning D9002 : ignoring unknown option '-std=c++2a'
Experimenting with some templates and their uses:
I was working on this simple struct here:
template<typename T, size_t n> // templated but not variadic
struct myArray {
static const size_t SIZE = n;
T arr_[SIZE];
myArray() {} // Default Constructor
template<class... U> // Initialization Constructor
myArray( U pack... ) { // Templated with variadic parameters of
// type U = T upto SIZE = n;
arr_ = pack...;
}
};
I would like to use this in this manner or similar:
int main() {
myArray<char, 6> arr1{ 'a', 'b', 'c', 'd', 'e', 'f' };
myArray<int, 4> arr2{ 1, 2, 3, 4 };
// Or like this:
myArray<char, 6> arr1 = { 'a', 'b', 'c', 'd', 'e', 'f' };
myArray<int, 4> arr2 = { 1, 2, 3, 4 };
}
And in visual studio 2017RC I keep getting this compiler error:
1>------ Build started: Project: PracticeMath, Configuration: Debug Win32 ------
1>PracticeMath.cpp
1>c:\users\skilz80\documents\visual studio 2017\projects\practicemath\practicemath\practicemath.cpp(19): error C3520: 'U': parameter pack must be expanded in this context
1>c:\users\skilz80\documents\visual studio 2017\projects\practicemath\practicemath\practicemath.cpp(22): note: see reference to class template instantiation 'myArray<T,n>' being compiled
1>c:\users\skilz80\documents\visual studio 2017\projects\practicemath\practicemath\practicemath.cpp(41): fatal error C1903: unable to recover from previous error(s); stopping compilation
1>Done building project "PracticeMath.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Not sure what I am missing here or what to do within the initialization constructor.
A possible way to write that constructor is
template <class ... U>
myArray( U ... pack ) : arr_ { pack... }
{ }
Two errors in your code
(1) the ... is before the pack name in declaration; so
myArray ( U pack ... )
// ..............^^^ wrong
and
myArray ( U ... pack )
// .........^^^ correct
(2) there are a lot of modes to use a variadic pack but
arr_ = pack...;
isn't a correct one.
The usual way to use a variadic pack with a C-style array is in initialitation; that is, with constructor, in initialization list.
So
myArray( U ... pack ) : arr_ { pack... }
// .....................^^^^^^^^^^^^^^^^
But take in count that a constructor like this is dangerous because there isn't a check regarding the number of element of the pack (if greater than SIZE the behavior of the program is undefined (and this, usually, become: the program crash). A siple way to avoid this problem is add a static_assert() in the body of the constructor; something like
template <class ... U>
myArray( U ... pack ) : arr_ { pack... }
{ static_assert( sizeof...(U) <= N , "too much values" ); }
I have this code in my source:
template <std::size_t... Dims>
class DimensionPack {
public:
using Dimensions = std::index_sequence<Dims...>;
static const std::size_t total_dimensions = sizeof...(Dims);
std::vector<unsigned int> even_or_odd;
public:
DimensionPack() {
unsigned idx = 0;
for ( ; idx < total_dimensions; idx++ ) {
//MatrixDimensionOddOrEven mdoe( Dimensions[idx] );
//unsigned int val = mdoe.even_or_odd;
//even_or_odd.push_back( val );
}
}
};
The commented lines of code is the code in question. I'm not familiar with using std::indx_sequence<> and I've read the documentation from MSD on it and still not sure about it especially when it is used with the using directive.
This template class will be used as a parameter pack for other variadic template classes for storing and extracting the data from the variadic parameter list. Within this class's constructor I'm using this structure's constructor and private method to check values and to return its state of either being even or odd and getting the value from its public constant member:
struct MatrixDimensionOddOrEven {
const unsigned int even_or_odd;
explicit MatrixDimensionOddOrEven( unsigned int odd_or_even ) : even_or_odd( test( odd_or_even ) ) {}
private:
const unsigned int test( unsigned int value ) const {
if ( value == 0 ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << "invalid number: " << value << " must be >= 1.";
Logger::log( strStream, Logger::TYPE_ERROR );
throw ExceptionHandler( strStream );
}
return ( ((value % 2) == 0) ? ODD : EVEN );
}
};
The cpp file to this header file compiles, but when I go to compile another cpp file such as main that includes it; it fails to compile.
This is the current error messages that I'm getting:
1>------ Build started: Project: FileTester, Configuration: Debug Win32 ------
1> main.cpp
1>c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(44): error C2540: non-constant expression as array bound
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(41): note: while compiling class template member function 'DimensionPack<2,3,4>::DimensionPack(void)'
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\main.cpp(336): note: see reference to function template instantiation 'DimensionPack<2,3,4>::DimensionPack(void)' being compiled
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(60): note: see reference to class template instantiation 'DimensionPack<2,3,4>' being compiled
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\main.cpp(155): note: see reference to class template instantiation 'Matrix<float,2,3,4>' being compiled
1>c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(45): error C2228: left of '.even_or_odd' must have class/struct/union
1>c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(45): error C2789: 'val': an object of const-qualified type must be initialized
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(45): note: see declaration of 'val'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
It isn't so much the compiler errors that are giving me trouble; it is more about this syntax that is sort of new to me within the use of variadic templates and parameter packs. So how do I properly write the syntax to get the individual elements of this std::index_sequence<...> that is assigned to Dimensions with the using directive in order to pass those values to the constructor of the helper structure that is seen in the for loop within the DimensionPack class?
There is no need for index_sequence. This will do:
template <std::size_t... Dims>
class DimensionPack {
public:
std::vector<unsigned int> even_or_odd;
public:
DimensionPack()
: even_or_odd{MatrixDimensionOddOrEven{Dims}.even_or_odd...}
{
}
};
As a bonus, the beauty of it is that you don't need to push_back each element. You can directly initialize the vector with all the elements.
Why would you do this?
template <std::size_t... Dims>
class DimensionPack {
public:
using odd_dims = std::integer_sequence<bool,
std::enable_if_t<Dims!=0, bool>(Dims%2)...
>;
constexpr static std::array<bool, sizeof...(Dims)> get_odd_dims() {
return {{ (bool)(Dims%2)... }};
}
};
now odd_dims is a integer_sequence of which dimensions are even and odd.
get_odd_dims returns a constexpr array of bools of which dimensions are odd. This is easier to iterate over than a integer sequence.
The odd_dims type will fail to compile if any Dims are 0. No need for runtime checks.
Dynamic allocation here on a per-DimensionPack basis seems a very strange way to solve this.
I have a list of boost::tuple. I want to expose this tuple list to Java bindings through SWIG. But when I try to compile mt wrap.cxx, generated by SWIG, I get following errors:
d:\xyz\...\vector.h(115) : error C2678: binary '==' : no operator found which takes a left-hand operand of type 'const boost::tuples::tuple<T0,T1>' (or there is no acceptable conversion)
with
[
T0=std::string,
T1=std::string
]
c:\program files\microsoft visual studio 8\vc\platformsdk\include\guiddef.h(192): or 'int operator ==(const GUID &,const GUID &)'
while trying to match the argument list '(const boost::tuples::tuple<T0,T1>, const MyTuple)'
with
[
T0=std::string,
T1=std::string
]
d:\xyz\...\vector.h(111) : while compiling class template member function 'int Vector<T>::index(const T &) const'
with
[
T=MyTuple
]
d:\xyz\...\MyTuple_wrap.cxx(17731) : see reference to class template instantiation 'Vector<T>' being compiled
with
[
T=MyTuple
]
Can anyone tell me what I should do to resolve this issue?
It is unclear how you arrived at the error you've shown. boost::tuple is tricky to wrap by default and there doesn't seem to be any standard interface to it included with SWIG. In my tests I couldn't get close to the error you were seeing without manually writing an interface file.
I did however succeeded in wrapping boost's tuples using the following interface file:
%{
#include <boost/tuple/tuple.hpp>
%}
namespace boost {
template <typename T1=void, typename T2=void, typename T3=void>
struct tuple;
template <>
struct tuple<void,void,void> {
};
template <typename T1>
struct tuple<T1, void, void> {
tuple(T1);
%extend {
T1 first() const {
return boost::get<0>(*$self);
}
}
};
template <typename T1, typename T2>
struct tuple <T1, T2, void> {
tuple(T1,T2);
%extend {
T1 first() const {
return boost::get<0>(*$self);
}
T2 second() const {
return boost::get<1>(*$self);
}
}
};
template <typename T1, typename T2, typename T3>
struct tuple <T1,T2,T3> {
tuple(T1,T2,T3);
%extend {
T1 first() const {
return boost::get<0>(*$self);
}
T2 second() const {
return boost::get<1>(*$self);
}
T3 third() const {
return boost::get<2>(*$self);
}
}
};
}
Basically all it does is add accessor functions to each of the specialisations of tuple you might care about. It's sufficient to make it minimally useful in Java or some other language. You would want to expand on this to cover larger tuples. You probably want to make the member functions get/set if your tuples aren't intended to be immutable.
I was able to test this with a SWIG module:
%module test
%include "boost_tuple.i"
%template(TestTuple) boost::tuple<int, double, char>;
%template(SingleTuple) boost::tuple<char>;
%inline %{
boost::tuple<int, double, char> func1() {
return boost::make_tuple(3, 2.0, '1');
}
void test1(boost::tuple<int, double, char>) {
}
%}
Which worked as expected with the following Java:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
TestTuple t = test.func1();
System.out.println("1: " + t.first() + " 2: " + t.second() + " 3: " + t.third());
test.test1(test.func1());
test.test1(new TestTuple(0, 0.0, '0'));
}
}
I'm trying to expose the following c++ function to python using boost.python:
template <typename genType>
genType refract(
genType const & I,
genType const & N,
typename genType::value_type const & eta);
and what I got is this:
template<typename N>
N reflect(N const & i, N const & n, typename N::value_type const & eta)
{
return glm::N refract(i,n,eta);
}
BOOST_PYTHON_MODULE(foo)
{
def("reflect", reflect<float>);
def("reflect", reflect<double>);
}
and I have the following error when compiling:
error C2780: 'void boost::python::def(const char *,F,const A1 &,const A2 &,const A3 &)' : expects 5 arguments - 2 provided
How should I wrap it?
-----edit------
This works:
template<class T>
T floor(T x)
{
return glm::core::function::common::floor(x);
}
BOOST_PYTHON_MODULE(busta)
{
def("floor", floor<double>);
def("floor", floor<float>);
}
from the reference, floor() definition is the folowing:
template< typename genType >
genType floor (genType const &x)
I can build this as a DLL, then import it in python and use floor() from there. Life feels so nice...but..
This won't work, and I would like to understand why:
template<class genType >
genType reflect (genType i, genType n, genType eta)
{
return glm::core::function::geometric::refract(i, n,eta);
}
BOOST_PYTHON_MODULE(busta)
{
def("reflect", reflect<float>);
}
refract() definition is on the top of this post.
the error I get now is this:
1>foo.cpp(37): error C2893: Failed to specialize function template 'genType glm::core::function::geometric::refract(const genType &,const genType &,const genType::value_type &)'
1> With the following template arguments:
1> 'float'
1> foo.cpp(60) : see reference to function template instantiation 'genType
`anonymous-namespace'::reflect<float>(genType,genType,genType)' being compiled
1> with
1> [
1> genType=float
1> ]
1>
1>Build FAILED.
This is not the perfect answer since it requires abusing the type system and writing plenty of additional glue code.
You could try defining a wrapper template to decorate your target type such that it would have the necessary typedefs to satisfy the calling function (reflect).
This example demonstrates the failings of such an approach. Notice that this reflect function performs a simple addition; however, for C++ to recognize the operator+ for the templated type N, the wrapper must explicitly define it.
#include <iostream>
using namespace std;
template<class N>
N reflect(const N& n, const typename N::value_type& t)
{
return n + t;
}
template<class N>
struct wrapper
{
typedef N value_type;
wrapper(const N& n):_n(n){}
operator N& () { return _n; }
N operator+ (const N& r) const { return _n + r; }
N _n;
};
int main(int,char**)
{
cout << reflect( wrapper<double>(1), 2.0) << endl;
return 0;
}