compiler : visual c++ 2010
os : windows 7 64bits

learning template and encounter some problem

template<typename T>
void easyCopyImpl(T const &A, typename boost::enable_if_c< boost::is_array<T>::value>::type *temp = 0)
{
	std::cout<<"this is array"<<std::endl;
	std::cout<<typeid(T).name()<<std::endl;
	std::copy(A, A + sizeof_array(A), typename std::ostream_iterator<T>(std::cout, "") );
}

template<typename T>
void easyCopyImpl(T const &A, typename boost::enable_if_c< boost::is_class<T>::value>::type *temp = 0)
{
	std::cout<<"this is class"<<std::endl;
	//std::copy(A.begin(), A.end(), std::ostream_iterator<T>(std::cout, "") );
}

template<typename T>
void easyCopy(T const &A)
{
	easyCopyImpl(A);
}

int main()
{
  int A[] = {1, 2, 3, 4};
  easyCopy(A);
}

It would pop out a lot of error messages
What is the problem and how could I solve it?Thanks

Recommended Answers

All 7 Replies

What kind of errors do you get? Please post the first few.

I suspect that they have something to do with the conversion of an int* to another data type because in easyCopy() your parameter would be a int * const &, which is a pretty strange data type (a reference to a constant pointer to a non-const int, which I'm pretty sure isn't allowed). You may need to create a partial specialization that gets used for pointer data types. Unfortunately, I can't help you with that, I never have any luck with specializations.

Your problem is with this part (in the array version):

typename std::ostream_iterator<T>(std::cout, "")

First of all, you don't need the first typename keyword because std::ostream_iterator<T> is a type, not a dependent name (doesn't need the typename to resolve it). The main problem, however, is that T is an array type (specifically, int[4] ), which cannot work for the ostream iterator. You need a means to extract the value type of the array, for example, you can do:

template <typename T>
struct value_type_of_array {
  typedef T type;
};

template <typename T, std::size_t Size>
struct value_type_of_array< T[Size] > {
  typedef T type;
};

That will solve that problem.

Also, I generally prefer to put the sfinae switching on the return type as opposed to a default parameter (unless it is a constructor which has no return type). Also, you should prefer to get used to using the enable_if as opposed to the enable_if_c because the former is more portable across compilers (some compilers handle boolean operations in template arguments with some problems). So, your complete and working code can be:

#include <iostream>
#include <typeinfo>
#include <boost/utility.hpp>
#include <boost/mpl/or.hpp>
#include <boost/type_traits.hpp>

template <typename T, std::size_t Size>
std::size_t sizeof_array(const T (&)[Size]) {
  return Size;
};

template <typename T>
struct value_type_of_array {
  typedef T type;
};

template <typename T, std::size_t Size>
struct value_type_of_array< T[Size] > {
  typedef T type;
};

template<typename T>
typename boost::enable_if< 
  boost::is_array<T>,
void >::type easyCopyImpl(T const &A)
{
	std::cout<<"this is array"<<std::endl;
	std::cout<<typeid(T).name()<<std::endl;
	std::copy(A, A + sizeof_array(A), std::ostream_iterator< typename value_type_of_array<T>::type >(std::cout, "") );
}

template<typename T>
typename boost::enable_if< 
  boost::is_class<T>,
void >::type easyCopyImpl(T const &A)
{
	std::cout<<"this is class"<<std::endl;
	//std::copy(A.begin(), A.end(), std::ostream_iterator<T>(std::cout, "") );
}

template<typename T>
void easyCopy(T const &A)
{
	easyCopyImpl(A);
}

int main()
{
  int A[] = {1, 2, 3, 4};
  easyCopy(A);
}

thanks, your reply let me figure out how to solve it

template<typename T, size_t SIZE>
size_t const sizeof_array(T const(&)[SIZE]) { return SIZE; }

template<typename T>
void easyCopyImpl(T const &A, typename boost::enable_if_c< boost::is_array<T>::value>::type *temp = 0)
{
  std::cout<<"this is array"<<std::endl;
  std::cout<<typeid(T).name()<<std::endl;
  typedef typename boost::remove_extent<T>::type TYPE;
  std::cout<<typeid(TYPE).name()<<std::endl;
  std::cout<<sizeof_array(A)<<std::endl;
  std::copy(A, A + sizeof_array(A), typename std::ostream_iterator<TYPE>(std::cout, "") );
}

template<typename T>
void easyCopyImpl(T const &A, typename boost::enable_if_c< boost::is_class<T>::value>::type *temp = 0)
{
  std::cout<<"this is class"<<std::endl;
  typedef typename T::value_type TYPE;
  std::copy(A.begin(), A.end(), typename std::ostream_iterator<TYPE>(std::cout, "") );
}

template<typename T>
void easyCopy(T const &A)
{
  easyCopyImpl(A);
}

dear mike_2000_17, thanks for your advices
I use boost::remove_extend instead of self define type traits
besides, do c++11 support the enable_if like boost did?
I check on msdn, their enable_if act like enable_if_c

I refine my codes, please tell me where should I refine if you think it should be.
Thank you very much

template<typename T, size_t SIZE>
size_t const sizeof_array(T const(&)[SIZE]) { return SIZE; }

template<typename T>
typename boost::enable_if< boost::is_array<T>, void>::type easyCopyImpl(T const &A)
{
  std::cout<<"this is array"<<std::endl;
  typedef typename boost::remove_extent<T>::type TYPE;
  std::cout<<typeid(TYPE).name()<<std::endl;
  std::cout<<sizeof_array(A)<<std::endl;
  std::copy(A, A + sizeof_array(A), std::ostream_iterator<TYPE>(std::cout, "") );
  std::cout<<std::endl;
}

template<typename T>
typename boost::enable_if< boost::is_class<T>, void>::type easyCopyImpl(T const &A)
{
  std::cout<<"this is class"<<std::endl;
  typedef typename T::value_type TYPE;
  std::copy(A.begin(), A.end(), std::ostream_iterator<TYPE>(std::cout, "") );
  std::cout<<std::endl;
}

template<typename T>
void easyCopy(T const &A)
{
  easyCopyImpl(A);
}

Template could provide better performance, smaller code size, better type system and compile time diagnosis
But someone(or many) people would argue that generic programming is very difficult to read and debug

Do you think boost concept is a good way to solve the defects of template?
I mean hard to debug, just look at the error messages generated by visual c++ 2010(error messages of gcc are
much more easier to decipher than msvc).

About difficult to read, I don't know how to solve this problem.
Frankly, the syntax of template may not very elegant.
But it is not too difficult to decipher after I get used to it
Besids, there are so many resources on the internet, just like daniweb
Yet I still remember the feeling at the first time I looked at those "ugly" syntax

Do you think it is a good idea to apply generic programming in a big project?
I don't think oop is easier to extend or maintain than static polymorphism if
I don't need the flexibility of heterogeneous polymorphism.
I could use static polymorphism to replace runtime polymorphism.

>>dear mike_2000_17, thanks for your advices

You're welcome.

>>I use boost::remove_extent instead of self define type traits

Great! I didn't know about remove_extent (I don't know everything!). Of course, if something exists already, use it.

>>besides, do c++11 support the enable_if like boost did?

Yes, well sort-of. The C++11 standard does include std::enable_if , like it is documented on msdn (I've actually been pleasantly surprised with the nice C++11 class documentations that msdn have been putting out). And yes, the std::enable_if is essentially equivalent to the boost::enable_if_c (which is OK because C++11 has stricter rules for constant-expressions, which was lacking in C++03 and caused some compatibility problems with some compilers that had a harder time with bool-expressions (like a || !b ) appearing as the bool template argument).

>>I refine my codes, please tell me where should I refine if you think it should be.

It looks pretty good to me. Although I don't like the use of all upper-case (as in TYPE) for anything other than a MACRO or #define. If you want to avoid the somewhat awkward use of the T::value_type and the std::ostream_iterator<TYPE> , if you have C++11 support for range-based for-loops, then you can use this instead:

#include "boost/concept_check.hpp"

template<typename T>
typename boost::enable_if< boost::is_array<T>, void>::type easyCopyImpl(T const &A)
{
  std::cout << "A is an array" << std::endl;
  for(auto x : A) std::cout << x;
  std::cout << std::endl;
}

template<typename T>
typename boost::enable_if< boost::is_class<T>, void>::type easyCopyImpl(T const &A)
{
  BOOST_CONCEPT_ASSERT((boost::Container<T>));

  std::cout << "A is a class" << std::endl;
  for(auto x : A) std::cout << x;
  std::cout << std::endl;
}

>>Template could provide:
>> better performance,
It does. The awesome thing with generic programming is that you get most of the benefits of polymorphism and more, without a performance penalty over hand-crafted code.

>> smaller code size,
It does and does not. It does entail some additional coding for Sfinae switches, concept-check classes, long lists of template arguments, and meta-functions. However, the actual code (that executes at run-time) that you have to write is minimal because you can write one piece of code that applies to a much wider number of types and use-cases (i.e. code reusability is very high, as high as it can get). But also, the generated binaries can be quite a bit larger due to a higher level of function inlining and the code-generation for each template instantiation.

>> better type system
It does. Often in OOP, you have to bend the rules of type safety (e.g. with dynamic_casts or "dummy" base class types) which will either lead to type-checks being delegated to run-time or to type-checks being circumvented entirely, either way it is not great. Generic programming is entirely type-safe in that regard (which doesn't mean you can't make mistakes, of course, but more errors can be caught at compile-time as opposed to at run-time or not at all).

>> and compile time diagnosis
Diagnosing problems with template-based code is definitely not easy. There are tools to parse the errors / warnings of the compiler to a more human-readable form, if that can help. It takes a certain amount of experience with template-based code to develop a good eye for understanding errors. Also, having a compiler installed in your brain is also very useful, and it does practically end up being so after a while (like knowing exactly where typename is needed or not, and generally being able to tell exactly how a code will behave before compiling). Generally, do not compile code if you are not entirely sure (to the best of your knowledge) that it will compile and if you are not sure how it will behave, in other words, there is little place for a trial-and-error approach (because each trial-and-error iteration can be time-consuming).

>>Do you think boost concept is a good way to solve the defects of template?

Boost.Concept-Check is pretty awesome. I use it extensively. And yes, it does improve GP code quite a bit, in two main ways. First, it catches errors earlier (as opposed to deep in the function templates or implementation details). For instance, generally, errors that sometimes occur with STL classes (like your original mistake with feeding a int[4] to a ostream_iterator template) often generate a huge error message with deep nesting of "instantiated from.." messages. If concept-check was used by STL, the messages would be a lot shorter with very little depth and wouldn't expose as much of the details of the STL on each error message. The second advantage of concept-checks is that they provide a natural way to document the code in terms of what concepts a class models, what concepts type-arguments are required to model, and what characteristics a concept should have (valid expressions, axioms, sub-concepts, etc.).

Some of the biggest template wizards have proposed the addition of Concepts to C++0x. Bjarne Stroustrup pushed very forcefully to try and make Concepts an integral part of the new C++ standard, as a major new language feature (and seemed pretty pissed that it was "yanked out"). I am, like many others, very sad that it did not make it into the new standard, because I think that, although Boost.Concept-Check is nice, to really implement Concepts correctly and neatly, native language support will be needed. And ConceptGCC seems to be a bit abandonned too.

>>I mean hard to debug, just look at the error messages generated by visual c++ 2010

Yeah, I have always hated the un-helpfulness of MSVC error / warning messages, not only with templates. GCC is simply clearer and to the point.


>>About difficult to read, I don't know how to solve this problem.

Generally I try to look at some of these template techniques as what it is: compile-time code. And so, I write it as I would other code, that is, with proper nesting and indenting:

template<typename T>
typename boost::enable_if< 
  boost::is_array<T>, 
void>::type 
  easyCopyImpl(T const &A)

//instead of:
template<typename T>
typename boost::enable_if< boost::is_array<T>, void>::type easyCopyImpl(T const &A)

That improves readability very much when coding.

One thing that I don't like, however, is the lack of support of heavily templated code in documentation systems like doxygen, which produces OK but pretty hard to read documentation for function and class templates with many template arguments.

>> Frankly, the syntax of template may not very elegant.
>> But it is not too difficult to decipher after I get used to it

That's true, a lot of the "unreadability" of templates is dependent on the training of the reader (e.g. C code is very hard to decipher for someone with no experience in programming, so why expect templates to be easy to decipher for someone with no experience with templates).

>>Do you think it is a good idea to apply generic programming in a big project?

All the biggest libraries that I know of use it extensively. For a few examples:
1) The STL uses generic programming all the way throughout.
2) Boost libraries are almost all a mix of generic programming and template meta-programming.
3) Most C++ linear algebra packages use a mix of GP and TMP, like Eigen, uBlas, and MTL.

One of the nice things with generic programming is that it is fairly easy to make the user-side code very simple and easy, while hiding a lot of complex stuff under-the-hood, and doing so with minimal run-time overhead (or none). Look at the STL for instance, the vast majority of people that use the STL are far from being able to actually understand how it's programmed under-the-hood (e.g. you don't need to know how tag-dispatching works to be able to use std::advance() ).

The main problem can be that it might be hard to find programmers to work on the project or library code itself that are competent enough to deal with GP and TMP techniques. But, I think that as long as you have a few good people, it isn't hard to train the rest (i.e. I have found that the learning curve from "competent C++ OOP programmer" to "competent C++ GP programmer" is not very steep, compared to other parts of the C++ learning curve).

>>I don't think oop is easier to extend or maintain than static polymorphism

I second that. I often find that OOP code is very inflexible compared to GP, it has a nasty tendency to lock you into a class hierarchy that is hard to adapt, change or extend beyond what you initially anticipated would be part of the library.

>>I don't need the flexibility of heterogeneous polymorphism.

What do you mean by "heterogeneous" polymorphism?

The main decision between static or dynamic polymorphism is whether you need to determine types at run-time or not. Also, OOP and GP are very easy to mix together (e.g. by having classes that are both a model of some generic concept to be used with some generic algorithms, and a polymorphic base class or derived class in an OOP class hierarchy), they are not mutually exclusive. But, certainly, if you don't need run-time flexibility, don't pay the price for it, go for generic programming instead.

What do you mean by "heterogeneous" polymorphism?

sorry, I thought it is same as dynamic polymorphism
I should say, if I don't need to deal with heterogeneous collection
like this example

struct shape
{
    virtual void draw() const { std::cout<<"this is shape"<<std::endl; }
    virtual ~shape() {}
    ...............
};

struct line : public shape
{
    virtual void draw() const { std::cout<<"this is a line"<<std::endl; }
    virtual ~line(){}
    ...............
};

struct ball : public shape
{
    virtual void draw() const { std::cout<<"this is a ball"<<std::endl; }
    virtual ~ball() {}
    ...............
};

struct Draw
{
    void operator()(shape const *const A) const { A->draw(); }
};

int main()
{  
  std::vector<shape*> coll;
  shape *A = new line;
  shape *B = new ball;
  coll.push_back(A);
  coll.push_back(B);
  std::for_each(coll.begin(), coll.end(), Draw() ); 

  return 0;
}

static polymorphism is very hard to deal with the heterogeneous collection like this, although boost::any could do something with that
But I would use virtual rather than boost::any

It looks pretty good to me. Although I don't like the use of all upper-case (as in TYPE) for anything other than a MACRO or #define.

this behavior inheritance from the coding style of verilog
I was an EE student and decided to become a hardware engineer in the past time
but I find out that I like the way of software more than hardware
because software is much more flexible and I could learn it well even without those
extraordinary expensive CAD tools.


pd :synthesis is too complicated for computers
although high-level synthesis bring us some hopes
but I don't think it will become the main stream
of the hardware design in a short time.

Boost.Concept-Check is pretty awesome. I use it extensively.

looks like it would be a good investment. Let us hope that 2016 would come up a better concepts
Is the meaning of this "concepts" same as the "concepts" mention in the book "generic programming and the stl"?

higher level of function inlining and the code-generation for each template instantiation

template instantiation could be control by casting(if I like to do that), the choice between speed and space
but I am not familiar with the inlining problem, inline is a suggestion for the compiler
the compiler would inline the codes even we didn't suggest it to do that?

>>Is the meaning of this "concepts" same as the "concepts" mention in the book "generic programming and the stl"?

Yes. There are a few additional things like concept maps and axioms, but the basic idea of concepts is the same. The STL currently simply documents what concepts the types should model but doesn't enforce them (e.g. most STL algorithms require that the iterator types model one of the iterator concepts (like ForwardIterator or RandomAccessIterator) if they don't, the compiler will still select the function and try to instantiate it, and will fail to do so at the first use of a function or operator that your iterator type doesn't have). The main idea of the proposed "concepts" is to be able to prevent, early, a class or function template from being instantiated if the given type-arguments don't model the required concepts, also selecting function template overloads in the same manner as enable_if (i.e. with Sfinae) based on modeled concepts (which you cannot do currently, you have to use meta-functions like "is_forward_iterator<Iter>" instead of something like "is_valid_concept< ForwardIterator<Iter> >", or you have to use an extra level of indirection like with the tag-dispatching). The Boost.Concept-Check library only allows you to assess of a type models a given concept (with BOOST_CONCEPT_ASSERT) but you cannot do Sfinae or template specializations based on the modeled concepts of the given types. Axioms are an addition and they simply don't exist right now at all, the idea of Axioms is to be able to verify that some relationship hold between functions or variables (e.g. to verify that a class is a "distance-metric", you could verify the axiom of "triangular inequality", like "static_assert( dist(a,c) <= dist(a,b) + dist(b,c) )"). Axioms are interesting, but I'm not sure how useful it really is.


>>but I am not familiar with the inlining problem, inline is a suggestion for the compiler. the compiler would inline the codes even we didn't suggest it to do that?

Templated code is essentially always considered as implicitly marked with the "inline" keyword. Except for some exceptional cases (explicit instantiation), all templated functions (i.e. function templates or member functions of class templates) are automatically considered candidates for inlining by the compiler. But, of course, the decision to inline is for the compiler, but when a larger number of functions are candidates for inlining, you end up with a lot more functions being inlined, which can cause either larger or smaller binaries depending on the cases, but generally they are larger because when optimization is turned on, the compiler prefers to generate more efficient code (and inlining is a very significant optimization).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.