Here's the deal. I've looked on this forum and I didn't find the information I'm searching for or I'm probably not able to repeat it for my problem. I have a class Table which is generic and I have a class named MyString.
template <typename typeGen, int DIM>
class Table {
public:
TableauGenerique() : index_(0) { //On initialise courant à 0
}
void add(typeGen type);
private:
typeGen tableGen_[DIM];
int index_;
};
My problem is with the add function.
I sometimes have to do this in the main.cpp: (which works well)
Table <float,6> tabFloat;
tabFloat.add(1.6564);
and at one point, I need to do this which doesn't work because I need to specialize the add function to create an object of MyString, to pass it the string and then store the object in the array (tableGen) :
TableauGenerique <MyString,4> tabString;
So I tried this (after the class), without success.
template <typename typeGen, int DIM>
void Table<typeGen,DIM>::add(typeGen type){ //Which is the generic one for float or ints
if(index_ < DIM) {
tableGen_[courant_] = type;
index_++;
}
}
template <class typeGen, int DIM>
void Table<typeGen,DIM>::add<string>(typeGen type) { //(line 75) Which is the specific or specialized function for myString
MyString str(type);
if(index_ < DIM) {
tableGen_[courant_] = str;
index_++;
}
}
So, How can I make this work because it doesn't compile at all, saying: line75 : error: expected initializer before '<' token and in the main it says not matching function to call Table::add(const char[6]),
I hope everything is clear enough. Let me know if somethings is unclear.
Thank you very much for your help !
template <class typeGen, int DIM>
void Table<typeGen,DIM>::add<string>(typeGen type)
You're trying to specialize add() when in fact it is not a function template to begin with. How do you expect it to work?
You probably meant: (specialization of the class)
template <int DIM>
void Table<string,DIM>::add(string type)
But then this is allowed only if you specialize the class itself. Without specializing the class, the above code would give compilation error!
EDIT:
You can read these online tutorials:
Introduction to C++ Templates
14.5 — Class template specialization
Template Specialization and Partial Template Specialization
Explicit specialization (C++ only)
If you can control the code of the MyString class, you can provide constructors that act as implicit conversions from float to MyString. An example:
#include <string>
#include <sstream>
#include <iostream>
class MyString {
public:
MyString(float number) {
std::stringstream buffer;
buffer << number;
value = buffer.str();
}
void print() {
std::cout << value << std::endl;
}
private:
std::string value;
};
template <class T>
class Foo {
public:
void DoStuff(T item) {
item.print();
}
};
int main() {
Foo<MyString> foo;
foo.DoStuff(1.342); // implicitly converts float to MyString
return 0;
}
This way, you do not need any specialization of the add method. However, implicit conversions are tricky, and you have be careful not to invoke them accidentally, and they may create ambiguities.
EDIT: Upon a second thought, my suggestion below is basically equivalent to
Table<MyString,4> tabString;
tabString.add(MyString("whatever"));
and therefore excessive and/or does not solve the problem. Feel free to ignore :)
I would extend the class Table with a generic method to add something from which you can construct an object of the desired type:
template <typename typeGen, int DIM>
class Table {
public:
Table() : index_(0) {}
void add(typeGen type);
// The additional method
template<typename T> void add(const T& src);
private:
typeGen tableGen_[DIM];
int index_;
};
template<typename typeGen, int DIM>
template<typename T>
void Table<typeGen,DIM>::add(const T& src) {
if(index_ < DIM) {
tableGen_[courant_] = typeGen(src);
index_++;
}
}
Note construction of a temporary typeGen object before the assignment.
Assuming that MyString object can be constructed from a string literal, i.e. from const char*, you can then use it as following:
Table<MyString,4> tabString;
tabString.add("whatever");
or if the above assumption is wrong, the following should probably work (because you constructed a MyString instance from a string instance):
tabString.add(string("whatever"));
Related
I am running into a design issue with my code and I am not sure where to go. I am attempting to write a basic I/O class to write vectors of data into a text file for convenience in my research. In practice, I would like to not worry about the datatype in the array that I am writing to file and use the same interface regardless of if it is an array of ints, doubles, etc.
My basic idea was to create an AbstractColumn class, with a templated derived class to handle columns of different data. My I/O class could then contain an array of pointers to objects of this Abstract Class, and I can can add to this array as needed. See my header file below for the implementation of this.
#ifndef BASICIO_H
#define BASICIO_H
#include <vector>
#include <cstring>
#include <string>
#include <stdlib.h>
struct AbstractColumn{
virtual ~AbstractColumn() = 0;
template <class T> T* get_data(); // Issue comes in here, I think
int nrows=0;
};
template <class T>
class Column : public AbstractColumn {
public:
Column(std::vector<T>& v);
~Column();
T* get_data() {return data;}
T* data;
int nrows;
};
template <class T>
Column<T>::Column(std::vector<T>& v){
nrows = v.size();
data = static_cast<T*>(malloc(sizeof(T)*v.size())); //malloc returns void* so we need the static cast
std::memcpy(data, v.data(), v.size());
}
template <class T>
Column<T>::~Column() {}
class BasicIO {
public:
BasicIO(std::string outname) : fname(outname), ncols(0) {}
~BasicIO();
template <class T>
void attach(std::vector<T>& v, std::string name="");
void write();
inline int get_ncols() {return ncols;}
inline std::string filename() {return fname;}
private:
std::vector<AbstractColumn*> columns;
std::vector<std::string> column_names;
std::string fname;
int ncols;
};
template <class T>
void BasicIO::attach(std::vector<T>& v, std::string name){
Column<T>* col = new Column(v);
columns.push_back(col);
column_names.push_back(name);
}
#endif
My issue is coming in in trying to write the write() method that actually dumps this data to a file.
void BasicIO::write(){
// other formatting code here
for(int i = 0; i<columns.at(0)->nrows; i++){
for(int j = 0; j<ncols; j++){
outfile<<std::scientific<<std::left<<std::setw(col_width)<<(columns.at(j)->get_data())[i]<<" ";
} //^ this is causing an error
outfile<<std::endl;
}
outfile.close();
}
When I try to compile, I get the an error saying note: template argument deduction/substitution failed and couldn't deduce template parameter 'T'. Now, I think I understand what the issue is. When the compiler reaches this point, all it doesn't know what type of data is going to be returned by my get_data() function, since in the base class the return type is the template T. However, I don't see a way around this and I am very stuck. I have done a bit of reading and it seems like type erasure might be the way to go, but I also think I might be missing something simpler.
My Question
All of the above context aside, my concrete question is as follows: What is the best way to have an array which holds objects (in this case columns) of generic types, specifically for the application described above? I am happy to offer any other details as needed, I just felt that my code snippets were already quite long. Thank you for reading.
I have the incomplete class below (but the necessary is there to understand my concern).
The following method is copying the content of a given bag (called sac in my code)
template <class T, int capInitial>
Sac<T,capInitial>& Sac<T,capInitial>::
operator+=(Sac &b) {
for(int i=0; i<b.getTaille(); i++){
*this += b.sac[i]; //LINE i DON'T UNDERSTAND
}
return *this;
}
Since the class Below has 2 attributes and a pointer to an array. In the line mentioned above what mechanism enable to take all elements in the given argument and just add it to the array of the class via *this+=b.sac[i], i would have done it in the following way
for(int i=0; i<b.getTaille(); i++){
sac[taille++]= b.sac[i];
Or perhaps there is something i don't quite understand? here is the incomplete class
template <class T, int capInitial>
class IterateurSac;
template <class T, int capInitial=64>
class Sac {
private:
T* sac;
int taille;
int capacite;
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution;
void augmenterCapacite(int cap);
void copier(const Sac &b);
public:
Sac() : taille(0), capacite(capInitial), generator(7437843) {
sac= new T[capacite];
}
template <class T, int capInitial>
Sac<T,capInitial>& Sac<T,capInitial>::
operator+=(const T &element) {
if (taille==capacite)
augmenterCapacite(2*capacite);
sac[taille++]= element;
return *this;
}
}
In C++, you can create your own behaviour for operators. Your class would need to overload operator+= to make this work, and that function would name the member variable to modify.
Indeed, we can see from your first code snippet that the class is already overloading operator+= to take an argument of type Sac<T,capInitial>&; it must have another one taking an argument of type T (or compatible).
What's not clear is why the class definition you later show us does not include a declaration for either of those functions.
I have the following simple class
generic<typename T> where T:IDbConnection ref class CDbConnection
{
private:
IDbConnection^m_db;
ConnectionState^ m_originalConnState;
public:
CDbConnection();
bool Connect(String ^ connStr);
bool Exists(int id);
auto GetAllData(String^ tableStr);
~CDbConnection();
!CDbConnection();
};
and here is my constructor
generic<typename T> CDbConnection<T>::CDbConnection()
{
m_db=gcnew T();
m_originalConnState=m_db->State;
}
But the compiler complains <1> the gcnew T() can't be used for generic type
<2> auto key in use is wrong as the function expects a trailing return type
Thank you for your reading and replies
I forgot this
where T:IDbConnection, gcnew()
which is exactly the same as C# generics
to get rid of the gcnew error as stated above.
In order to achieve genericity, you must change your class definition to
generic<typename T> where T:IDbConnection ref class CDbConnection
{
private:
T m_db;
ConnectionState^ m_originalConnState;
public:
CDbConnection();
bool Connect(String ^ connStr);
bool Exists(int id);
auto GetAllData(String^ tableStr);
~CDbConnection();
!CDbConnection();
};
As you are already constraining your T to be at least IDbConnection it can't be anything else.
Then your constructor
generic<typename T> CDbConnection<T>::CDbConnection()
{
m_originalConnState=m_db.State;
}
should work like you intended.
EDIT
It seems you cannot declare a reference to a generic. If you assign the object to the stack it will work.
See this entry.
// C3229.cpp
// compile with: /clr /c
generic <class T>
ref class C {
T^ t; // C3229
};
// OK
generic <class T>
ref class D {
T u;
};
Suppose i have a function template StrCompare
template<typename T=NonCaseSenCompare>//NonCaseSenCompare is a user defined class look at the detailed code below.
int StrCompare(char* str1, char* str2)
{
...
}
now in the main function i write a line
char* str1="Zia";
char* str2="zia";
int result=StrCompare(str1,str2);
it should work because we have provided a default template argument, but it does'nt compiler gives the following error
no matching function for call to `StrCompare(char*&, char*&)'
Now the detailed code is given by
#include<iostream.h>
class CaseSenCompare
{
public:
static int isEqual(char x, char y)
{
return x==y;
}
};
class NonCaseSenCompare
{
public:
static int isEqual(char x,char y)
{
char char1=toupper(x);
char char2=toupper(y);
return char1==char2;
}
};
template<typename T=NonCaseSenCompare>
int StrCompare(char* str1, char* str2)
{
for(int i=0;i < strlen(str1)&& strlen(str2);i++)
{
if(!T::isEqual(str1[i],str2[i]))
return str1[i]-str2[i];
}
return strlen(str1)-strlen(str2);
}
main()
{
char* ptr1="Zia ur Rahman";
char* ptr2="zia ur Rahman";
int result=StrCompare(ptr1,ptr2);//compiler gives error on this line
cout<<result<<endl;
system("pause");
}
If I write
int result=StrCompare<>(ptr1,ptr2);
compiler gives the same error message.
As gf and AndreyT already wrote, you can't have default template arguments with function templates. However, if you turn your comparators into function objects, you can still use default function arguments:
template<typename Comp>
int StrCompare(char* str1, char* str2, Comp = NonCaseSenCompare())
{
...
}
You can now call StrCompare() like this
StrCompare("abc","aBc",CaseSenCompare());
or like this:
StrCompare("abc","aBc"); // uses NonCaseSenCompare
A comparator would then have to look like this:
struct CaseSenCompare {
bool operator()(char x, char y) const {return x==y;}
};
Adjust StrCompare() accordingly.
§14.1/9:
A default template-argument shall not
be specified in a function template
declaration or a function template
definition, nor in the
template-parameter-list of the
definition of a member of a class
template.
A simple work-around would be to move it into a class:
template<typename T=NonCaseSenCompare>
struct StrCompare {
static int compare(char* str1, char* str2) { /* ... */ }
};
Firstly, function templates do not support default template arguments, only class templates do.
Secondly, even when all class template parameters have default arguments, you still have to specify an empty <> to refer to that class template.
What i use is next trick;
lets say you want to have function like this
template <typename E, typename ARR_E = MyArray_t<E> > void doStuff(ARR_E array)
{
E one(1);
array.add( one );
}
you will not be allowed, but i do next way:
template <typename E, typename ARR_E = MyArray_t<E> >
class worker {
public:
/*static - as you wish */ ARR_E* parr_;
void doStuff(); /* do not make this one static also, MSVC complains */
};
template <typename E, typename ARR_E>
void worker::doStuff<E, ARR_E>::getChunks()
{
E one(1);
parr_->add( one );
}
so this way you may use it like this.
MyArray_t my_array;
worker<int> w;
w.parr_ = &arr;
w.doStuff();
as we can see no need to explicitly set second parameter.
maybe it will be useful for someone.
I have a container class, we'll call it
template <class T> CVector { ... }
I want to do something different with this class when T is a pointer type, e.g. something along the lines of:
template <class T*> CVector< SomeWrapperClass<T> >;
where SomeWrapperClass is expecting the type of the pointed to thing as its parameter. Unfortunately, this syntax doesn't quite work and with some digging, I haven't found a good way to get something like this working.
Why do it this way? I want to change, in a very large app, how some of our containers work when the type they're specialized on is a pointer vs. not a pointer - and ideally, i'd like to do it without changing the ~1,000 places in the code where there are things like CVector<Object*> vs CVector<int> or some such - and playing games with partial specializations seemed to be the way to go.
Am I on crack here?
If I understand you correctly, this might do what you want:
template<typename T>
class CVector { ... };
template<typename T>
class CVector<T*> : public CVector< SomeWrapperClass<T> > {
public:
// for all constructors:
CVector(...) : CVector< SomeWrapperClass<T> >(...) {
}
};
It adds an additional layer of inheritance to trick CVector<T*> into being a CVector< SomeWrapperClass<T> >. This might also be useful in case you need to add additional methods to ensure full compatibility between the expected interface for T* and the provided interface for SomeWrapperClass<T>.
This works just fine in C++...
#include <iostream>
template <class T>
class CVector
{
public:
void test() { std::cout << "Not wrapped!\n"; }
};
template <class T>
class CVector<T*>
{
public:
void test() { std::cout << "Wrapped!\n"; }
};
int main()
{
CVector<int> i;
CVector<double> d;
CVector<int*> pi;
CVector<double*> pd;
i.test();
d.test();
pi.test();
pd.test();
}
I don't think you can specialize a class using the syntax you describe... I don't know how that could possibly work. What you can do is specialize the class for pointers and re-implement its guts using the wrapper class around the raw pointers. I'm not sure if it will help, but this article describes specializing templates for pointers.
The Boost type traits library can help you achieve this. Check out the is_pointer type trait.
#include <boost/type_traits.hpp>
#include <iostream>
#include <vector>
using namespace std;
template <class T>
class CVector {
public:
void addValue(const T& t) {
values_.push_back(t);
}
void print() {
typedef boost::integral_constant<bool,
::boost::is_pointer<T>::value> truth_type;
for (unsigned int i = 0; i < values_.size(); i++)
doPrint(values_[i], truth_type());
}
private:
void doPrint(const T& t, const boost::false_type&) {
cout << "Not pointer. Value:" << t << endl;
}
void doPrint(const T& t, const boost::true_type&) {
cout << "Pointer. Value: " << *t << endl;
}
std::vector<T> values_;
};
int main() {
CVector<int> integers;
integers.addValue(3);
integers.addValue(5);
integers.print();
CVector<int*> pointers;
int three = 3;
int five = 5;
pointers.addValue(&three);
pointers.addValue(&five);
pointers.print();
}
I don't think templates are quite that flexible.
A very brute force approach would be to specialize for all of your pointer types...which defeats the problem of using templates.
Could you have a different CVector class that is used only for vectors of pointers?
I agree with rlbond's answer. I have modified it a little bit to suit your need. CVector can be a derived class of the CVector itself. You can then use different members and functions for it.
#include <iostream>
#include <string>
template <class T>
class CVector
{
public:
void test() { std::cout << "Not wrapped!\n"; }
void testParent() { std::cout << "Parent Called\n";}
};
template <class T>
class CVector<T*>:
public CVector<T>
{
public:
void test(std::string msg) { std::cout << msg; testParent(); }
};
int main()
{
CVector<int> i;
CVector<double> d;
CVector<int*> pi;
CVector<double*> pd;
i.test();
d.test();
pi.test("Hello\n");
pd.test("World\n");
system("pause");
}