I have a template function defined as follows:
template<class T>
string toString(T value) {
ostringstream ss;
if (is_same<T, Student>::value) {
ss << value.getFirst() << ":" << value.getLast() << ":" << value.getId() << ":" << value.getGpa();
return ss.str();
}
else {
//ss << value;
return ss.str();
}
}
If i were to call this function like so:
int main(){
Student studentObj;
toString(studentObj);
}
How do i access this classes various members from the toString function?
I have tried (errors commented)
value.getId() //Returns int
//Error C2228 left of '.getId' must have class/struct/union
and
value<Student>.getId()
//Error C2275 'Student': illegal use of this type as an expression
Thanks in advance!
edit: Class definition
class Student {
protected:
std::string firstname;
std::string lastname;
int id;
float gpa;
public:
Student();
Student(std::string, std::string, int, float);
Student(const Student &);
std::string getFirst();
std::string getLast();
int getId();
float getGpa();
};
No. You cannot do that. With second phase of template code compilation for any non-Student type the if part will fail to compile. Not that if is runtime, not compile time, even though std::is_same is compile time. When you call it as toString(10) the compiler still has to compile it fully for int. It won't evaluate runtime if statement and eliminate the if (true) block - compiler still has to compile it, and produce object code for it. And hence the error.
You just need to specialize it:
template<class T>
string toString(T value)
{
ostringstream ss;
/// skipped code
return ss.str();
}
// SPECIALIZE for 'Student'
template<>
std::string toString(Student s)
{
// Code here
}
Add const and/or & if you want to.
To access the member functions of a class through template function, try calling this way.
Student studentobj;
std::string temp = toString<Student>(studentobj); // This will invoke the member functions of student class
// Code snippet similar to your query
class test
{
public:
test(){}
void callme()
{
std::cout<<"In callme function"<<std::endl;
}
};
template<class T>
void sample(T obj)
{
std::cout<<"In sample function"<<std::endl;
obj.callme();
}
int _tmain(int argc, _TCHAR* argv[])
{
test obj;
sample<test>(obj);
return 0;
}
Output:
In sample function
In callme function
Related
Or perform any function really.
Currently I have some code that looks a bit like this:
int temp_int;
streamer >> temp_int;
finalObj = static_cast<CustomType>(temp_int);
ints can be cast into CustomTypes (obviously) and finalObj is of type CustomType
streamer's >> operator has not been set up to work with CustomType. Changing this is outside my area of control.
Im looking for a way to put the above code in a single line and avoid using a temporary variable.
When putting custom types back into the streamer, I use this:
streamer << static_cast<int>(someObj);
Thats a lot neater. And it would be great to have the instreaming and outstreaming look symmetrical for easier reading.
Thanks
Just implement an operator>> and make sure name lookup finds it.
struct CustomType
{
int m_value;
CustomType(int v = 0)
: m_value(v)
{}
operator int() const
{
return m_value;
}
};
namespace
{
std::istream& operator>>(std::istream& s, CustomType& out)
{
int temp;
s >> temp;
out = temp;
return s;
}
}
int main()
{
CustomType finalObj;
std::cin >> finalObj;
std::cout << finalObj << '\n';
}
Alternatively, if you aren't sure, if operator>> has already been implemented somewhere in an undesired manner, you could create a wrapper type and implement operator>> for it.
namespace
{
class CustomTypeParsingWrapper
{
public:
CustomTypeParsingWrapper(CustomType& target)
: m_target(target)
{
}
friend std::istream& operator>>(std::istream& s, CustomTypeParsingWrapper const& wrapper)
{
int temp;
s >> temp;
wrapper.m_target = temp;
return s;
}
private:
CustomType& m_target;
};
}
int main()
{
CustomType finalObj;
std::cin >> CustomTypeParsingWrapper(finalObj);
std::cout << finalObj << '\n';
}
Note: The use of the anonymous namespace here is only to avoid conflicts with implementations provided to other translation units. If you want to reuse operator>>, you should put it in the same namespace as CustomType to allow Argument Dependent Lookup to find it. A customTypeParsingWrapper intended to be reused could be put in a namespace of your choice...
One way to achieve what you want is to overload not only << and >> operators, but also the cast operator.
One way to do that is to overload the int() operator for CustomType. This way, you can use put your value back into the streamer easily:
class CustomType
{
private:
int _value;
public:
CustomType(const int &value) : _value(value) {}
// ...
operator int() const { return _value; }
};
This way, the compiler will find the appropriate cast from CustomType to int when using the << operator:
streamer << someObj;
will implicitly cast to int.
Similar overloads could be implemented to achieve the results.
Here is a full snipped to test this:
#include <iostream>
class CustomType
{
private:
int _value;
public:
CustomType(const int &value) : _value(value) {}
// ...
operator int() const { return _value; }
};
int main() {
auto someObj = CustomType(10);
std::cout << someObj;
}
This prints 10 to the standard output.
I am learning design patterns and I am trying to implement builder pattern. For this purpose I am compiling below code with "clang++ -std=c++17" but I am getting error "error: initialization of incomplete type 'HtmlBuilder'" where I am returning in static function HtmlElement::build. How to solve this issue?
class HtmlBuilder;
class HtmlElement
{
friend class HtmlBuilder;
string name, text;
vector<HtmlElement> elements;
const size_t indent_size = 2;
HtmlElement() {}
HtmlElement(const string &name, const string &text): name(name), text(text) {}
public:
string str(int indent = 0) const
{
ostringstream oss;
string i(indent_size*indent, ' ');
oss << i << "<" << name << ">" << endl;
if (text.size() > 0)
{
oss << string(indent_size * (indent + 1), ' ') << text << endl;
}
for (const auto& e: elements)
{
oss << e.str(indent + 1);
}
oss << i << "</" << name << ">" << endl;
return oss.str();
}
static HtmlBuilder build(const string& root_name)
{
return {root_name};
}
};
class HtmlBuilder
{
HtmlElement root;
public:
HtmlElement build()
{
return root;
}
HtmlBuilder(const string& root_name)
{
root.name = root_name;
}
HtmlBuilder& add_child(const string& child_name, const string& child_text)
{
HtmlElement e{child_name, child_text};
root.elements.emplace_back(e);
return *this;
}
string str() const
{
return root.str();
}
};
int main(int argc, char const *argv[])
{
HtmlBuilder builder("ul");
builder.add_child("li", "apple").add_child("li", "orange");
cout << builder.str() << endl;
return 0;
}
Edit - build returns object instead of reference.
As the first comment points out you need to separate the declaration from the definition, otherwise the compiler is forced to tackle both at once, as they're given.
In your original code:
class HtmlBuilder;
class HtmlElement
{
friend class HtmlBuilder;
public:
static HtmlBuilder build(const string& root_name)
{
return {root_name};
}
};
Here the compiler is being instructed to construct and return something it only knows by name, it knows nothing about how to create one of these. That's where you get the error. If instead you split this up and have a header file like:
// html_element.h
class HtmlBuilder;
class HtmlElement
{
friend class HtmlBuilder;
public:
static HtmlBuilder build(const string& root_name);
};
Here it's understood that something called HtmlBuilder is involved, and it's not clear what that is, but that's fine so long as it's eventually explained.
Then later in your implementation file:
#include "html_builder.h"
#include "html_element.h"
HtmlBuilder HtmlElement::build(const string& root_name)
{
return {root_name};
}
This means it doesn't need to know exactly how to make one, it just has the general idea, and by the time your implementation is addressed it's all clear because the header file has been processed.
Note that you should not return a reference to a temporary as JaMiT points out, so I've removed that from this code.
It's a good practice to split declarations (.h or .hpp as you prefer) from definitions/implementations (.cpp) to avoid traps like this. It also makes it a lot easier to find things when working with your code.
Today I'm working on a singleton test case in c++.
The singleton is working fine but I would like to instantiate the static object when the user try to access a member of it, so if the variable isn't created when we try to access a member of it, it will not crash instead it will simply generate my singleton.
Here's my class.h:
class PDG : public EmployeRH
{
public:
static void Instantiate(std::string nom, std::string prenom);
// Current manual instantiation version of the singleton
PDG* operator->();
// This is the line I just added to overload "->" operator ... But it seems it's never called.
void SePresenter();
static PDG* _instance;
private:
PDG();
~PDG();
PDG(std::string nom, std::string prenom);
int _budget;
};
Methods.cpp
PDG* PDG::_instance=NULL;
PDG::PDG()
{
}
PDG::~PDG()
{
}
PDG::PDG(std::string a_nom, std::string a_prenom):EmployeRH(a_nom,a_prenom)
{
_budget = 100000;
}
void PDG::Instantiate(std::string a_nom, std::string a_prenom)
{
cout << "instantiation pdg" << endl;
if (_instance == NULL)
{
_instance = new PDG(a_nom,a_prenom);
}
}
PDG* PDG::operator->()
{
PDG::Instantiate("Unknown", "Unknown");
return _instance;
}
void PDG::SePresenter()
{
cout << _nom << " " << _prenom << endl;
}
main.cpp
void main()
{
PDG::_instance->SePresenter();
system("pause");
}
The thing is, it goes directly into "SePresenter()" and not into my overloaded operator "->".
If anyone could help it would be greatfull.
Thanks,
Impact
PDG::_instance is a pointer to PDG so -> simply dereferences the pointer and you can't override the behaviour. To override the -> operator you must call it on the class directly not on a pointer: (*PDG::_instance)->SePresenter(). To preserve your desired syntax and to remove the undefined behaviour from dereferencing the null pointer you can change PDG::_instance into a structure which holds your instance pointer.
#include <string>
#include <iostream>
using namespace std;
struct EmployeRH {
EmployeRH() {}
EmployeRH(std::string nom, std::string prenom) {}
std::string _nom;
std::string _prenom;
};
class PDG : public EmployeRH {
public:
static PDG* Instantiate(std::string nom, std::string prenom);
// Current manual instantiation version of the singleton
void SePresenter();
static struct Instance {
PDG* operator->()
{
return PDG::Instantiate("Unknown", "Unknown");
}
} _instance;
private:
PDG();
~PDG();
PDG(std::string nom, std::string prenom);
int _budget;
};
PDG::Instance PDG::_instance;
PDG::PDG()
{
}
PDG::~PDG()
{
}
PDG::PDG(std::string a_nom, std::string a_prenom)
: EmployeRH(a_nom, a_prenom)
{
_budget = 100000;
}
PDG* PDG::Instantiate(std::string a_nom, std::string a_prenom)
{
static PDG instance(a_nom, a_prenom);
cout << "instantiation pdg" << endl;
return &instance;
}
void PDG::SePresenter()
{
cout << _nom << " " << _prenom << endl;
}
int main()
{
PDG::_instance->SePresenter();
return 0;
}
I've also changed your singleton to use a function static which makes your code thread safe.
I'm experimenting a little bit with templated classes and I have found this error:
Error C2039 'getName': is not a member of 'Person<T>
This is my class structure:
struct professor
{
static const char name[];
static const age = 50;
};
struct student
{
static const char name[];
static const age = 21;
};
const char professor::name[] = "Jules";
const char student::name[] = "Michael";
// Please make the Person template class here.
template <class T>
class Person
{
public:
Person();
char[] getName();
private:
T & m_Person;
};
template<class T>
Person<T>::Person()
{
m_Person= T;
}
template<class T>
char* Person<T>::getName()
{
return m_Person.name;
}
Do you know what is failing?
In fact I don't know if the class definition etc are correct because I'm quite new with the templated classes so If you see any other error It would be great If you warn me.
THank you, I hope you can help me.
Fix compilation errors from first to last. char[] is not a valid return type, the function definition for getName() fails to compile that's why you get the error.
You are also missing the type specifier for the age member variables as C++ does not support default int.
Your code is a little confusing, I think you want something like this:
#include <string>
#include <iostream>
struct professor
{
std::string name;
int age;
};
struct student
{
std::string name;
int age;
};
template <class T>
class Person
{
public:
Person(const T& person) : m_Person(person) {}
std::string getName()
{
return m_Person.name;
}
private:
T m_Person;
};
int main() {
student s{"Michael", 21};
Person<student> p(s);
std::cout << p.getName();
return 0;
}
If you want to use a class with only static members as a template parameter, you don't need to store an instance:
#include <iostream>
struct S {
static const int x = 42;
};
template <typename T>
struct A {
int getValue() {
return T::x;
}
};
int main() {
A<S> a;
std::cout << a.getValue();
return 0;
}
I got that message
no matching function for call to 'main()::MySeqInFileEnumerator::MySeqInFileEnumerator(const char [10])'
when im doing my string matching job.I have to method override existing code.I have to open an input text, and make an abstract file from it, then i have to do an optimistic linsearch.
#include <iostream>
#include "linsearch.hpp"
#include "seqinfileenumerator.hpp"
using namespace std;
struct MyPair
{
int azon;
int osszeg;
friend ifstream& operator>>(ifstream& f, MyPair& df);
};
ifstream& operator>>(ifstream& f, MyPair& df)
{
f >> df.azon >> df.osszeg;
return f;
}`enter code here`
int main()
{
class MyLinSearch: public LinSearch <int, true>
{
bool Cond(const int& e) const
{
return e<=-100000;
}
};
class MySeqInFileEnumerator: public SeqInFileEnumerator <MyPair>
{
void Next()
{
MyPair dx;
f >> dx;
df.azon=dx.azon;
df.osszeg=dx.osszeg;
while(dx.azon==df.azon)
{
dx.osszeg+=df.osszeg;
f >> dx;
}
}
};
MyLinSearch pr;
MySeqInFileEnumerator t("input.txt");
pr.AddEnumerator(&t);
pr.Run();
if (pr.Found())
{
cout << "false " << endl;
}
else cout << "true" << endl;
return 0;
}
As the error message says, the class has no constructor taking a string; yet you try to use one with
MySeqInFileEnumerator t("input.txt");
Perhaps the base class has a suitable constructor? In that case, you'll need to forward the argument:
explicit MySeqInFileEnumerator(char const * name) :
SeqInFileEnumerator<MyPair>(name)
{}
You forgot to add a suitable constructor. Something like this:
class MySeqInFileEnumerator: public SeqInFileEnumerator<MyPair>
{
public:
MySeqInFileEnumerator(char const * p) : SeqInFileEnumerator<MyPair>(p) { }
// ...
};
(This is assuming your base class has a corresponding constructor. Modify to taste.