Unfortunately the behavior I want to figure out is relevant to multiple compilation units. So I cannot provide a simple example to run in ideone/coliru. But I did write up a minimal code example.
Note this code as written here does compile and work. My questions are about why certain changes made to it don't work, and how to properly structure this code to meet my goals.
Update: The secondary question has been answered by @dyp. Only the question in the title remains. Thanks!
header:
// header
#include <iostream>
#include <sstream>
using namespace std;
struct stream {
std::stringstream ss;
string str() const;
};
class htmlstream : public stream {
};
class jsonstream : public stream {
};
template <typename STREAM>
class writer {
STREAM s;
public:
template <typename T> friend writer<STREAM>& operator<<(writer<STREAM>&, const T&);
int write();
};
template <typename T> htmlstream& operator<<(htmlstream& hs, const T& t) {
hs.ss << "<div>" << t << "</div>";
return hs;
}
template <typename T> jsonstream& operator<<(jsonstream& js, const T& t) {
js.ss << "{ \"t\": " << t << " }";
return hs;
}
// this solves undefined reference to writer<htmlstream>& op<< <int>() linker error
// I want to write an operator<< that is generic both in the parameter of writer and in
// the type T
template <typename T> writer<htmlstream>& operator<<(writer<htmlstream>& w, const T& t) {
w.s << t;
return w;
}
#if 0
// why doesn't this work? (I am just missing something simple I think)
template <typename T, typename STREAM> writer<STREAM>& operator<<(writer<STREAM>& w, const T& t) {
w.s << t;
return w;
}
#endif
implementation cpp (includes header):
string stream::str() const {
return ss.str();
}
template <typename STREAM> int writer<STREAM>::write() {
cout << "writing: " << s.str() << endl;
return 0;
}
// instantiation of writer -- I need to have this otherwise linker error. I want this to be declared generally so I can cover all descendants of stream in one statement.
template class writer<htmlstream>;
calling code cpp (includes header):
int main() {
writer<htmlstream> hw;
hw << 4;
cout << hw.write() << endl;
return 0;
}
Question 1:
- As you can see I have multiple derived
streams
(htmlstream
andjsonstream
). I don't want to have to writetemplate class writer<htmlstream>;
as well astemplate class writer<jsonstream>;
. I would like to useenable_if
andis_base_of
to achieve this. How? Also, why does compilation fail if I writetemplate<> class writer<htmlstream>
instead?
Question 2 (answered in comments):
- Notice the
#if 0
commented template function that I attempted to write, which compiles but fails to match anything. I want to be able to specify anoperator<<
that works on all writer parameterized types as well as all right-hand-side argument types. Is this possible? How is it written? (What keywords do I use to Google this?)
(mini-question) I also would like to lift things out of the header into the implementation file if possible. But it seems like this cannot be done for many of the templates. Maybe I just need to try declaring these templates extern
in the header?