boost::any is a strong concept and a much better replacement to void* to hold any type of data. You can make heterogenous containers using it as well. Let us see how it works in a very simplified way. The idea is to have a template class that can wrap all types and a value associated with that type. Something like this:
template<typename T>
class HoldData
{
T t;
};
And then having a base class from which this wrapper would derive, so the above becomes adding a constructor that needs the type to be stored in it to be copy constructible:
class BaseHolder
{
public:
virtual ~BaseHolder(){}
};
template<typename T>
class HoldData : public BaseHolder
{
public:
HoldData(const T& t_) : t(t_){}
private:
T t;
};
Now, you would have a class, name it Variant that will take inputs of all types and then has a pointer to this wrapper's base type. So, now you have (including above classes):
class BaseHolder
{
public:
virtual ~BaseHolder(){}
};
template<typename T>
class HoldData : public BaseHolder
{
public:
HoldData(const T& t_) : t(t_){}
private:
T t;
};
class Variant
{
public:
template<typename T>
Variant(const T& t) : data(new HoldData<T>(t)){}
~Variant(){delete data;}
private:
BaseHolder* data;
};
You construct the corresponding type's wrapper objects and save their pointer into the another class that you call variant, that can hold and help retrieve any data type and does not lose the respective type information. That is actually what boost::any does. Take a look at the code here - boost::any code.
The documentation on it can be found here - boost::any documentation.
Showing posts with label templates. Show all posts
Showing posts with label templates. Show all posts
Friday, September 14, 2007
boost::any
Posted by
abnegator
at
9/14/2007 09:57:00 AM
4
comments
Labels:
boost,
boost::any,
heterogenous containers,
templates,
type-safe
Saturday, April 14, 2007
Operator overloading basics
It is quite true that operator overloading looks such a big deal for beginners. I had my problems with that as well when I started with C++ and kept ignoring it considering it to be a complex thingie. But I tell you it is not. For an initial introduction to it, you may find this Codeguru FAQ entry quite useful : C++ Operator: How to deal with operator overloading?
Hope you find it interesting and easy to follow.
Keep rocking! Cheers!
Hope you find it interesting and easy to follow.
Keep rocking! Cheers!
Posted by
abnegator
at
4/14/2007 04:39:00 PM
0
comments
Labels:
arithematic operators,
basics,
C++,
friends,
NRVO,
op in terms of op=,
operator overloading,
post-increment,
pre-increment,
RVO,
stream operators,
templates
Tuesday, February 13, 2007
Functors with state - 3 (print contents of vector using std::copy)
I had discussed an issue with function objects containing state and the problems they lead to (especially for std::remove_if algorithm) because of copy creation of those due to argument passing by value. The way they should be passed around is not guranteed by the standards. Here is the starting point for that discussion - Functors with state - 1.
There can be multiple things that can be improved in that code. The first one that I will pick up is the way the function "PrintVector" is laid down.
[CODE]
template<typename T>
void PrintVector(const std::vector<T>& t){
std::cout << std::endl << "Printing vector contents" << std::endl;
for(typename std::vector<T>::size_type i=0; i<t.size(); ++i){
std::cout << t[i] << '\t';
}
std::cout << std::endl << std::endl;
}
There are problems with this code. It is not flexible enough to let you choose the stream where you want the output to be pushed. Secondly, it uses loops and that loop leads to a third problem and that is the repeated call to vector<T>::size() function.
When we have algorithms at our disposal and feel they can improve the code, we should use them. Here is a better way to write the PrintVector function:
[CODE]
#include<vector>
#include<iostream>
#include<algorithm>
#include<iterator>
#include<string>
template<typename T>
void PrintVector(std::ostream& ostr, const std::vector<T>& t, const std::string& delimiter){
std::copy(t.begin(), t.end(), std::ostream_iterator<T>(ostr, delimiter));
}
A simple one liner!
Another way to better it could be making it independent of the container type. Currently it would work for std::vector only (as demanded as the second argument). We can use iterator inputs (forward iterators should suffice). Here:
[CODE]
#include<iostream>
#include<algorithm>
#include<iterator>
#include<string>
template<typename T, typename InputIterator>
void Print(std::ostream& ostr, InputIterator itbegin, InputIterator itend, const std::string& delimiter){
std::copy(itbegin, itend, std::ostream_iterator<T>(ostr, delimiter));
}
Now, this Print template can be used to print contents of any sequence that supports input iterators i.e. std::vector, std::deque, std::list, std::string and even plain arrays. The output stream can be anything, a file or the console or anything deriving from std::ostream and similarly we have generalized the delimiter between two subsequent elements that previously was a tab to any string.
More, later! Have fun!!
(Next installment here - Functors with state - 4)
There can be multiple things that can be improved in that code. The first one that I will pick up is the way the function "PrintVector" is laid down.
[CODE]
template<typename T>
void PrintVector(const std::vector<T>& t){
std::cout << std::endl << "Printing vector contents" << std::endl;
for(typename std::vector<T>::size_type i=0; i<t.size(); ++i){
std::cout << t[i] << '\t';
}
std::cout << std::endl << std::endl;
}
There are problems with this code. It is not flexible enough to let you choose the stream where you want the output to be pushed. Secondly, it uses loops and that loop leads to a third problem and that is the repeated call to vector<T>::size() function.
When we have algorithms at our disposal and feel they can improve the code, we should use them. Here is a better way to write the PrintVector function:
[CODE]
#include<vector>
#include<iostream>
#include<algorithm>
#include<iterator>
#include<string>
template<typename T>
void PrintVector(std::ostream& ostr, const std::vector<T>& t, const std::string& delimiter){
std::copy(t.begin(), t.end(), std::ostream_iterator<T>(ostr, delimiter));
}
A simple one liner!
Another way to better it could be making it independent of the container type. Currently it would work for std::vector only (as demanded as the second argument). We can use iterator inputs (forward iterators should suffice). Here:
[CODE]
#include<iostream>
#include<algorithm>
#include<iterator>
#include<string>
template<typename T, typename InputIterator>
void Print(std::ostream& ostr, InputIterator itbegin, InputIterator itend, const std::string& delimiter){
std::copy(itbegin, itend, std::ostream_iterator<T>(ostr, delimiter));
}
Now, this Print template can be used to print contents of any sequence that supports input iterators i.e. std::vector, std::deque, std::list, std::string and even plain arrays. The output stream can be anything, a file or the console or anything deriving from std::ostream and similarly we have generalized the delimiter between two subsequent elements that previously was a tab to any string.
More, later! Have fun!!
(Next installment here - Functors with state - 4)
Posted by
abnegator
at
2/13/2007 10:05:00 AM
2
comments
Labels:
C++,
copy,
iterators,
ostream_iterator,
templates,
vector
Monday, February 12, 2007
Quiz : function pointers as template arguments
Yesterday, I came across a piece of template code that took me a little by surprise (because I had not come across something like this before) but I was able to put my reasoning through. I will share the code first:
[CODE]
#include <iostream>
template<typename T>
void foo(const T&)
{
std::cout << "const";
}
template<typename T>
void foo(T&)
{
std::cout << "non-const";
}
void bar() { }
int main()
{
foo(bar);
}
The question was - what would the program print? Will the argument "bar" resolve as a parameter to the first template having argument type "const F&" or to the second template having non-const argument type "F&"?
The easiest way to check for the resolution is to ask for explicit template instantiation of the "foo" function template. How? Here is how:
[CODE]
int main()
{
typedef void (*f_ptr)(); //create a typedef for functions like bar
//taking no arguments and having return type as void.
foo<const f_ptr>(bar); //1
foo<f_ptr>(bar); //2
}
After that, just remove one of the "foo" templates. So, the code to check for compilation is this:
[CODE]
#include<iostream>
template<typename T>
void foo(T&)
{
std::cout << "non-const";
}
void bar() { }
int main()
{
typedef void (*f_ptr)(); //create a typedef for functions like bar
//taking no arguments and having return type as void.
foo<const f_ptr>(bar); //1
foo<f_ptr>(bar); //2
}
If you removed the second template, code compiles fine. But if you kept the second one and removed the first one, you will see that the compilation fails for mis-match in the argument type in statement "//2".
Problem solved. Isn't it? What does this tell about the argument "bar" ? It tells that it is a constant. And hence the call in the initial sample code would resolve to the template instance having the argument type declared const. It is in a way similar to any other type constants, for example 5, 100, 2000 are integral constants, they are literals. And when you declare something as say int i; here i is a variable that can be modified. but 5, 100, or 2000 cannot be. In our initial code, both the templates were capable of instantiating the right function for the argument. In both's presence, the argument match has to be exact and hence instantiation happens from the const one but in its absense the instantiation can happen even with the non-const one as the call can help the template to instantiate over type const f_ptr instead of just f_ptr (which is the case for the const one).
Functions pointers when being passed by taking the address of the function directly is a value of const function pointer type.
[CODE]
#include <iostream>
template<typename T>
void foo(const T&)
{
std::cout << "const";
}
template<typename T>
void foo(T&)
{
std::cout << "non-const";
}
void bar() { }
int main()
{
foo(bar);
}
The question was - what would the program print? Will the argument "bar" resolve as a parameter to the first template having argument type "const F&" or to the second template having non-const argument type "F&"?
The easiest way to check for the resolution is to ask for explicit template instantiation of the "foo" function template. How? Here is how:
[CODE]
int main()
{
typedef void (*f_ptr)(); //create a typedef for functions like bar
//taking no arguments and having return type as void.
foo<const f_ptr>(bar); //1
foo<f_ptr>(bar); //2
}
After that, just remove one of the "foo" templates. So, the code to check for compilation is this:
[CODE]
#include<iostream>
template<typename T>
void foo(T&)
{
std::cout << "non-const";
}
void bar() { }
int main()
{
typedef void (*f_ptr)(); //create a typedef for functions like bar
//taking no arguments and having return type as void.
foo<const f_ptr>(bar); //1
foo<f_ptr>(bar); //2
}
If you removed the second template, code compiles fine. But if you kept the second one and removed the first one, you will see that the compilation fails for mis-match in the argument type in statement "//2".
Problem solved. Isn't it? What does this tell about the argument "bar" ? It tells that it is a constant. And hence the call in the initial sample code would resolve to the template instance having the argument type declared const. It is in a way similar to any other type constants, for example 5, 100, 2000 are integral constants, they are literals. And when you declare something as say int i; here i is a variable that can be modified. but 5, 100, or 2000 cannot be. In our initial code, both the templates were capable of instantiating the right function for the argument. In both's presence, the argument match has to be exact and hence instantiation happens from the const one but in its absense the instantiation can happen even with the non-const one as the call can help the template to instantiate over type const f_ptr instead of just f_ptr (which is the case for the const one).
Functions pointers when being passed by taking the address of the function directly is a value of const function pointer type.
Posted by
abnegator
at
2/12/2007 04:35:00 PM
1 comments
Labels:
C++,
const,
constants,
exact match,
function objects,
function pointers,
literals,
overload resolution,
templates
Subscribe to:
Posts (Atom)
