Specify a base classes template parameters while instantiating a derived class? - c++

I have no idea if the title makes any sense but I can't find the right words to descibe my "problem" in one line. Anyway, here is my problem. There is an interface for a search:
template <typename InputType, typename ResultType> class Search {
public:
virtual void search (InputType) = 0;
virtual void getResult(ResultType&) = 0;
};
and several derived classes like:
template <typename InputType, typename ResultType>
class XMLSearch : public Search<InputType, ResultType> {
public:
void search (InputType) { ... };
void getResult(ResultType&) { ... };
};
The derived classes shall be used in the source code later on. I would like to hold a simple pointer to a Search without specifying the template parameters, then assign a new XMLSearch and thereby define the template parameters of Search and XMLSearch
Search *s = new XMLSearch<int, int>();
I found a way that works syntactically like what I'm trying to do, but it seems a bit odd to really use it:
template <typename T> class Derived;
class Base {
public:
template <typename T>
bool GetValue(T &value) {
Derived<T> *castedThis=dynamic_cast<Derived<T>* >(this);
if(castedThis)
return castedThis->GetValue(value);
return false;
}
virtual void Dummy() {}
};
template <typename T> class Derived : public Base {
public:
Derived<T>() {
mValue=17;
}
bool GetValue(T &value) {
value=mValue;
return true;
}
T mValue;
};
int main(int argc, char* argv[])
{
Base *v=new Derived<int>;
int i=0;
if(!v->GetValue(i))
std::cout<<"Wrong type int."<<std::endl;
float f=0.0;
if(!v->GetValue(f))
std::cout<<"Wrong type float."<<std::endl;
std::cout<<i<<std::endl<<f;
char c;
std::cin>>c;
return 0;
}
Is there a better way to accomplish this?

Is there a better way to accomplish
this?
Yes, that design is slightly better, since that's using static-dispatching while calling GetValue() (I'm assuming that dynamic_cast is typo, you actually wanted to type static_cast in Base::GetValue()). In that design, Base::GetValue() is not virtual, yet it is able to call Derived::GetValue() using pointer of type Base. This makes it slightly fast.
But even your way is not that bad. All you've to instantiate your class templates like this:
Search<int,int> *s = new XMLSearch<int, int>();
Your Search *s = new XMLSearch<int, int>() is wrong!
You can typedef your templates as follows:
typedef Search<int,int> iisearch;
typedef XMLSearch<int,int> iixmlsearch;
Then use them:
iisearch *s = new iixmlsearch();
This looks better, right?
Small Modification
You can make your class slightly better performance-wise. For that, write your Search class template as follows:
template <typename InputType, typename ResultType> class Search {
public:
void search (InputType input) //it's not virtual anymore!
{
xmlsearch *_this = getXmlSearch();
xmlsearch->search(input);
}
void getResult(ResultType& result) //it's not virtual anymore!
{
xmlsearch *_this = getXmlSearch();
xmlsearch->getResult(result);
}
private:
typedef XMLSearch<InputType, ResultType> xmlsearch;
xmlsearch* getXmlSearch()
{
static xmlsearch *_this= static_cast<xmlsearch* >(this);
return _this;
}
};
Now your base class is not abstract, as it doesn't define virtual functions. This design is slightly faster than your version!

Related

Derive (virtual) function arguments in variadic template class

I'm building an interpreter and trying to avoid some boilerplate I run into when implementing builtin-functions. I am able to to do this by using templates.
Take this base template for instance:
template<ast::builtin_type T>
class builtin_procedure abstract : public builtin_procedure_symbol
{
using arg_traits = builtin_type_traits<T>;
protected:
builtin_procedure(const symbol_identifier& identifier): builtin_procedure_symbol(identifier)
{
this->register_param(arg_traits::param_id(), T);
}
/**
* The actual implementation of the built-in function
*/
virtual void invoke_impl(typename arg_traits::builtin_type) = 0;
public:
void invoke(scope_context& procedure_scope) override
{
auto raw_arg = procedure_scope.memory->get(procedure_scope.symbols.get(arg_traits::param_id()));
this->invoke_impl(arg_traits::get_from_expression(raw_arg));
}
};
To implement a built-in function function that takes a string, I only need to do:
class builtin_procedure_writeln final : public builtin_procedure<ast::builtin_type::string>
{
protected:
void invoke_impl(arg_traits::builtin_type arg) override;
public:
builtin_procedure_writeln();
}; /* Implementation in cpp file */
Very convenient, I only need to implement the virtual invoke_impl method and that's it.
I'm trying to wrap my head around getting this implemented with a variable number of template arguments so I don't have to duplicate my template definition if I want to support 2, 3, or more arguments in my derived implementation like in the example below.
This would be the template above to support a second template parameter:
template<ast::builtin_type T1, ast::builtin_type T2>
class builtin_procedure abstract : public builtin_procedure_symbol
{
using arg1_traits = builtin_type_traits<T1>;
using arg2_traits = builtin_type_traits<T2>;
protected:
builtin_procedure(const symbol_identifier& identifier): builtin_procedure_symbol(identifier)
{
this->register_param(arg_traits::param_id(1), T1);
this->register_param(arg_traits::param_id(2), T2);
}
/**
* The actual implementation of the built-in function
*/
virtual void invoke_impl(typename arg1_traits::builtin_type, typename arg2_traits::builtin_type) = 0;
public:
void invoke(scope_context& procedure_scope) override
{
auto raw_arg1 = procedure_scope.memory->get(procedure_scope.symbols.get(arg1_traits::param_id()));
auto raw_arg2 = procedure_scope.memory->get(procedure_scope.symbols.get(arg2_traits::param_id()));
this->invoke_impl(arg1_traits::get_from_expression(raw_arg1), arg2_traits::get_from_expression(raw_arg2));
}
};
I know that essentially through template recursion you can essentially iterate through each of the template parameters to do whatever you want to do, but what about the definition of the virtual invoke_impl method? Each of the parameters are derived from the the traits struct, and the call to the method itself also seems not something you could some with template recursion.
How (if) it possible to use a variadic template to allow for a variable number of arguments on this base class as an alternative to just copy/paste this base class with more template arguments?
The final clue was given n314159, this works:
template<ast::builtin_type... Ts>
class builtin_procedure abstract : public builtin_procedure_symbol
{
private:
template<ast::builtin_type T>
typename builtin_type_traits<T>::builtin_type make_arg(scope_context& procedure_scope, int param_id)
{
auto raw_arg = procedure_scope.memory->get(procedure_scope.symbols.get(builtin_type_traits<T>::param_id(param_id++)));
return builtin_type_traits<T>::get_from_expression(raw_arg);
}
protected:
builtin_procedure(const symbol_identifier& identifier, ::symbol_table* runtime_symbol_table): builtin_procedure_symbol(identifier, runtime_symbol_table)
{
auto param_id = 0;
((void) this->register_param(builtin_type_traits<Ts>::param_id(++param_id), Ts), ...);
}
virtual void invoke_impl(typename builtin_type_traits<Ts>::builtin_type...) = 0;
public:
void invoke(scope_context& procedure_scope) override
{
auto param_id = 0;
this->invoke_impl(make_arg<Ts>(procedure_scope, ++param_id)...);
}
};
So, I wrote a small example. I don't think one can do aliasing for variadic templates, so I left that out, but it works without even if it is less nice. So, since I can't use non-integral non-type template parameters, I switched your ast::builtin_type to int, but I think you can reverse that easily enough. The following compiles (but doesn't link, obviously^^).
template<int i>
struct builtin_traits {
static int param_id(int) { return i;}
using builtin_type = int;
};
class builtin_procedure_symbol {
void register_param(int, int);
};
int get(int); // my replacement for procedure_scope.memory->get(procedure_scope.symbols.get
template<int... Ts>
class builtin_procedure : builtin_procedure_symbol{
builtin_procedure(): builtin_procedure_symbol()
{
((void) this->register_param(builtin_traits<Ts>::param_id(1), Ts), ... );
}
virtual void invoke_impl(typename builtin_traits<Ts>::builtin_type...) = 0;
void invoke()
{
auto f = [&](const auto& arg) {
auto raw_arg = get(builtin_traits<arg>::param_id());
return builtin_traits<arg>::get_from_expression(raw_arg);
};
this->invoke_impl(f(Ts)...);
}
};
I hope that helps you. If something is unclear, please ask.

c++ virtual templated function

Understandably, functions can not be both templated and virtual.
But there may be a super smart design pattern out there that would do.
My goal is to have a function which looks like as :
void configure(const Configuration &config){
double stuff = config.get<double>("stuff");
int thing = config.get<int>("thing");
// rest of the code
}
Ideally, I could pass various configuration object, e.g. object that read from a file or from a database.
Here a (stripped to minimum) example of a concrete config class using yaml-cpp (I guess understandable even if you do not know yaml-cpp):
class YAML_config : public Configuration {
public:
YAML_config(std::string file_path){
this->node = YAML::LoadFile(file_path);
}
template<typename T> T get(std::string key){
return this->node[key].as<T>();
}
private:
YAML::Node node;
Question is: what would be the suitable code for the class Configuration ?
Here some invalid code that shows the intend:
class Configuration {
virtual template<typename T> T get(std::string key)=0;
}
If all this is just a bad start, any other approach I should look into ? I checked for "type erasure", but that did not seem to help (or did I miss something ?)
It looks like you have a small-ish set of possible types, so I suggest a set of virtual functions grouped together with a non-virtual dispatching template:
template <class T>
struct tag { };
class Configuration {
public:
template <class T>
T get(std::string key) {
return get_(tag<T>{}, std::move(key));
}
protected:
virtual int get_(tag<int>, std::string key) = 0;
virtual double get_(tag<double>, std::string key) = 0;
virtual std::string get_(tag<std::string>, std::string key) = 0;
};
class YAML_config : public Configuration {
int get_(tag<int>, std::string key) override { /* ... */ }
double get_(tag<double>, std::string key) override { /* ... */ }
std::string get_(tag<std::string>, std::string key) override { /* ... */ }
};
Usage:
YAML_config cfg;
auto s = cfg.get<int>("hello");
See it live on Coliru
But we lost the ability to declare YAML_config::get as a template -- types aside, the implementations are all the same, but we can't override a virtual function with a template.
So, now that we bridged the gap from templates to virtual functions to achieve polymorphism, let's bridge the gap from virtual functions back to templates to get our nice API back. This can be done by slotting in a CRTP between the Configuration and YAML_config classes: its role will be to generate the overriden functions.
Note: the get_ virtual functions are now called getBridge. I have added a dash of macros to cut down on repetition. These can be further factored out with Boost.PP, for example.
class ConfigurationBase {
// ...
#define DECLARE_CONFIG_BRIDGE(T) \
virtual T getBridge(tag<T>, std::string key) = 0;
DECLARE_CONFIG_BRIDGE(int)
DECLARE_CONFIG_BRIDGE(double)
DECLARE_CONFIG_BRIDGE(std::string)
#undef DECLARE_CONFIG_BRIDGE
};
template <class Derived>
class Configuration : public ConfigurationBase {
// Hide ConfigurationBase::get so we don't get
// infinite recursion if we forget an implementation
// in the derived class.
template <class>
void get(...) = delete;
#define OVERRIDE_CONFIG_BRIDGE(T) \
T getBridge(tag<T>, std::string key) override { \
return dThis()->template get<T>(std::move(key)); \
}
OVERRIDE_CONFIG_BRIDGE(int)
OVERRIDE_CONFIG_BRIDGE(double)
OVERRIDE_CONFIG_BRIDGE(std::string)
#undef OVERRIDE_CONFIG_BRIDGE
Derived *dThis() {
return static_cast<Derived*>(this);
}
};
class YAML_config : public Configuration<YAML_config> {
public:
template <class T>
T get(std::string) {
return {};
}
};
See it live on Coliru
I have adapted my answer to a similar question from earlier today which uses type erasure and RTTI to get the effect of a virtual templated function. As I noted there, Boost.TypeIndex can be used if you cannot or do not want to use RTTI.
The basic implementation looks something like this (just fill in your YAML library stuff):
#include <functional>
#include <typeindex>
#include <unordered_map>
class config {
public:
template <typename T>
T get(char const* key) {
T value = {};
auto it = getters.find(type_index<T>());
if (it != getters.end()) {
it->second(&value, key);
}
return value;
}
protected:
template <typename T, typename Getter>
void register_getter(Getter getter) {
getters[type_index<T>()] = [getter](void* value, char const* key) {
*static_cast<T*>(value) = getter(key);
};
}
private:
template <typename T>
static std::type_index type_index() {
return std::type_index(typeid(std::remove_cv_t<T>));
}
std::unordered_map<std::type_index, std::function<void (void*, char const*)>> getters;
};
Usage would look like this (note that you could use composition instead of inheritance if you don't actually need config to be a base class):
#include <iostream>
class yaml_config : public config {
public:
yaml_config() {
register_getter<int>([](char const* key) {
return 42;
});
register_getter<float>([](char const* key) {
return 3.14f;
});
}
};
int main() {
yaml_config cfg;
std::cout << cfg.get<int>("foo") << "\n";
std::cout << cfg.get<float>("bar") << "\n";
std::cout << cfg.get<short>("baz") << "\n";
}
Output:
42
3.14
0
In this particular implementation, T must be default constructible; if this is unacceptable, you could use std::any instead of void*. In addition, a default value is returned in the case where an appropriate getter is not registered. You may want to throw an exception, or return a std::optional<T> or std::pair<T, bool>, to distinguish these cases from a default value actually being mapped to a specific key.
This solution has the advantage that sub-classes can register getters for any type. However, there are certainly more efficient solutions if you know the subset of types that config::get<T> needs to work with.

class containing a generic type of a child

Is there any possible way that a generic type can be used to contain a child of a base class.
From the assignment given to me, I am to create something similar to the following in structure.
template <class T>
class Fruit {
private:
int count;
int location_id;
T type;
public:
virtual void displayInfo();
};
class Apple : private Fruit<Apple> {
private:
int variety;
public:
void displayInfo() {
printf("Location %i has %i of %s in stock", location_id, count, variety);
}
};
Fruit<Apple> appleinventory[SIZE];
Basically, I think you can't have a template generic type be the same as a derived class. Am I wrong? Is there something similar that would possibly work?
Update:
For the assignment, I believe we are to use inheritance to show use of virtual functions. I've updated the code above. I think this would work, but does NOT need templates to be successful. We have not covered any advanced, redundant inheritance methods in class.
This is perfectly fine, in principle.
Read up about Curiously Recurring Template Pattern (CRTP) for more info on usage of derived class as the instantiating type in a class template that is its base, esp the example about static polymorphism which should look 'curiously' familiar.
template <class Derived> struct Base
{
void interface()
{
// ...
static_cast<Derived*>(this)->implementation();
// ...
}
static void static_func()
{
// ...
Derived::static_sub_func();
// ...
}
};
struct Derived : Base<Derived>
{
void implementation();
static void static_sub_func();
};
Ignoring questions of why you want to do this....you can get some of the way by doing this following:
template <class T> class Fruit
{
private:
int count;
int location_id;
T* type;
};
class Apple : private Fruit<Apple>
{
private:
int seeds;
bool red;
};
Fruit<Apple> appleinventory[SIZE];
Note the T* type is now a pointer to Apple rather than an instance of Apple.

Creating an interface for an abstract class template in C++

I have the code as below. I have a abstract template class Foo and two subclasses (Foo1 and Foo2) which derive from instantiations of the template. I wish to use pointers in my program that can point to either objects of type Foo1 or Foo2, hence I created an interface IFoo.
My problem is I'm not sure how to include functionB in the interface, since it is dependant on the template instantiation. Is it even possible to make functionB accessible via the interface, or am I attempting the impossible?
Thank you very much for your help.
class IFoo {
public:
virtual functionA()=0;
};
template<class T>
class Foo : public IFoo{
public:
functionA(){ do something; };
functionB(T arg){ do something; };
};
class Foo1 : public Foo<int>{
...
};
class Foo2 : public Foo<double>{
...
};
You are actually attempting the impossible.
The very heart of the matter is simple: virtual and template do not mix well.
template is about compile-time code generation. You can think of it as some kind of type-aware macros + a few sprinkled tricks for meta programming.
virtual is about runtime decision, and this require some work.
virtual is usually implemented using a virtual tables (think of a table which lists the methods). The number of methods need be known at compile time and is defined in the base class.
However, with your requirement, we would need a virtual table of infinite size, containing methods for types we haven't seen yet and that will only be defined in the years to come... it's unfortunately impossible.
And if it were possible ?
Well, it just would not make sense. What happens when I call Foo2 with an int ? It's not meant for it! Therefore it breaks the principle that Foo2 implements all the methods from IFoo.
So, it would be better if you stated the real problem, this way we could help you at a design level rather than at a technical level :)
Easiest way is to make your interface templated.
template <class T>
class IFoo {
public:
virtual void functionA()=0;
virtual void functionB(T arg){ do something; };
};
template<class T>
class Foo : public IFoo<T>{
public:
void functionA(){ do something; };
void functionB(T arg){ do something; };
};
Since functionB's argument type must be known in advance, you have only one choice: Make it a type which can hold every possible argument. This is sometimes called a "top type" and the boost libraries have the any type which gets quite close to what a top type would do. Here is what could work:
#include <boost/any.hpp>
#include <iostream>
using namespace boost;
class IFoo {
public:
virtual void functionA()=0;
virtual void functionB(any arg)=0; //<-can hold almost everything
};
template<class T>
class Foo : public IFoo{
public:
void functionA(){ };
void real_functionB(T arg)
{
std::cout << arg << std::endl;
};
// call the real functionB with the actual value in arg
// if there is no T in arg, an exception is thrown!
virtual void functionB(any arg)
{
real_functionB(any_cast<T>(arg));
}
};
int main()
{
Foo<int> f_int;
IFoo &if_int=f_int;
if_int.functionB(10);
Foo<double> f_double;
IFoo &if_double=f_double;
if_int.functionB(10.0);
}
Unfortunately, any_cast does not know about the usual conversions. For example any_cast<double>(any(123)) throws an exception, because it does not even try to convert the integer 123 to a double. If does not care about conversions, because it is impossible to replicate all of them anyway. So there are a couple of limitations, but it is possible to find workarounds if necessary.
I don't think you can get what you want. Think of this if you were to implement your suggestion: if you have a pointer to an IFoo instance and you call functionB(), what type parameter should you give it? The underlying problem is that Foo1::functionB and Foo2::functionB have different signatures and do different things.
You can achieve something comparable by wrapping the IFoo* pointer in a class and exposing the functionality via generic template functions of the non-templated wrapper class:
#include <assert.h>
// interface class
class IFoo {
public:
virtual int type() const = 0; // return an identifier for the template parameter
virtual bool functionA() = 0;
};
// This function returns a unique identifier for each supported T
template <typename T> static int TypeT() { static_assert("not specialized yet"); }
template <> static int TypeT<bool>() { return 0; }
template <> static int TypeT<double>() { return 1; }
//template <> static int TypeT<...>() { ... }
// templated class
template <typename T> class FooT : public IFoo {
public:
int type() const override { return TypeT<T>(); }
bool functionA() override { return true; }
// not in interface
bool functionB(T arg) { return arg == T(); }
};
// function to create an instance of FooT (could also be static function in FooT)
static IFoo* CreateFooT(int type)
{
switch (type)
{
case 0: return new FooT<bool>();
case 1: return new FooT<double>();
//case ...: return new FooT<...>();
default: return nullptr;
}
}
// Non-templated wrapper class
class FooWrapper {
private:
IFoo *pFoo;
public:
FooWrapper(int type) : pFoo(CreateFooT(type)) { assert(pFoo != nullptr); }
~FooWrapper() { delete pFoo; }
bool functionA() { return pFoo->functionA(); }
template <typename T> bool functionB(T arg)
{
if(pFoo->type() != TypeT<T>())
{
assert(pFoo->type() == TypeT<T>());
return false;
}
return static_cast<typename FooT<T>*>(pFoo)->functionB(arg);
}
// fun stuff:
// (const pendants omitted for readability)
bool changeType(int type)
{
delete pFoo;
pFoo = CreateFooT(type);
return pFoo != nullptr;
}
IFoo* Interface() { return pFoo; }
IFoo* operator->() { return pFoo; }
operator IFoo&() { return *pFoo; }
template <typename T> FooT<T> *InterfaceT()
{
if(pFoo->type() != TypeT<T>())
{
assert(pFoo->type() == TypeT<T>());
return nullptr;
}
return static_cast<typename FooT<T>*>(pFoo);
}
};
int main(int argc, char *argv[])
{
FooWrapper w1(TypeT<bool>());
FooWrapper w2(TypeT<double>());
w1.functionA(); // ok
w2.functionA(); // ok
w1.functionB(true); // ok
w1.functionB(0.5); // runtime error!
w2.functionB(true); // runtime error!
w2.functionB(0.5); // ok
// fun stuff
w2.changeType(TypeT<bool>()); // older changes will be lost
w2.functionB(true); // -> now ok
w1.Interface()->functionA();
w1->functionA();
IFoo &iref = w1;
iref.functionA();
FooT<bool> *ref = w1.InterfaceT<bool>();
ref->functionB(true);
return 0;
}
It is of course your responsibility to call the functions with the correct types, but you can easily add some error handling.

C++ visitor pattern handling templated string types?

I'm trying to use the visitor pattern to serialize the contents of objects. However one snag I'm hitting is when I'm visiting strings. My strings are of a templated type, similar to STL's basic_string. So something like:
basic_string<char_type, memory_allocator, other_possible_stuff> \\ many variations possible!
Since I can have very many different templated string types, I can't go and add them to my visitor interface. It would be ridiculous. But I can't add templates to my VisitString method because C++ prevents using templates parameters in virtual methods.
So what are my options to work around this?
EDIT: I've added some basic code
class IVisitor
{
public:
virtual void VisitString(some_kind_of_string_type string) = 0; // this is what I want in theory
};
class MyObject
{
public:
typedef basic_string<char8, myAllocator, some_flag> MyStringType;
Accept(IVisitor* visitor)
{
visitor->VisitString(mString);
}
private:
MyStringType string;
};
class MyOtherObject
{
public:
typedef basic_string<char16, myOtherAllocator, some_other_flag> MyOtherStringType;
Accept(IVisitor* visitor)
{
visitor->VisitString(mString);
}
private:
MyOtherStringType string;
};
class Reader : public IVisitor
{
public:
virtual void VisitString(some_kind_of_string_type string)
{
// read some data, give it to the string
}
}
Do you need runtime polymorphism?
struct object {
template <typename Visitor>
void accept( Visitor & v )
{
v( x );
v( a );
}
int x;
std::string a;
};
struct complex_object {
template <typename Visitor>
void accept( Visitor & v ) {
v( i );
o.accept(v); // [1]
}
int i;
object1 o;
};
struct DumpScreenVisitor {
void operator()( int x ) { std::cout << x << std::endl; }
template <typename char_t, typename traits_t, typename alloc_t>
void operator()( std::basic_string<char_t, traits_t, alloc_t> const & str )
{
std::cout << str << std::endl;
}
};
The call in [1] can be converted into v( o ) with a generic templated operator() in the visitors that is the least specialized:
template <typename O>
void DumpScreenVisitor::operator()( O & o )
{
o.accept( *this );
}
But this can interfece with other visitor implementations (for example, the above visitor can be implemented with a single templated method):
struct DumpScreenVisitor {
template <typename T>
void operator()( T const & t ) {
std::cout << t << std::endl;
}
};
So at the end you will have to compromise in either way.
This approach is similar to the boost::variant visitor implementation (you may want to take a look at it), with the difference that the boost::variant is a single class and not a hierarchy.
In the end, I went with a slightly different approach. Instead of hoping to use a visitor with templated methods (which is, of course, impossible), I decided to pass a visitor-like class as a template parameter to my object's visit method. Totally simplified example:
class SomeKindOfVisitor // doesn't need to derive from a base class.
{
template <class StringClass>
void VisitString(StringClass& string) // I get to keep templated methods
}
class MyObject
{
typedef basic_string<char8, myAllocator, some_flag> MyStringType;
public:
template <class VisitorClass>
void Accept(VisitorClass& visitor)
{
vistior.VisitString<MyStringType>(mMyString);
}
private:
MyStringType mMyString;
}
With this method, I still get to use my templated strings while still being able to pass any kind of "visitor" to my objects.
your visitor should handle only a basic representation of strings (char* / wchar*);
it is then up to the accept method to process the cast.
Well, the question is, of the template parameters on your string can be so different, can you apply one single serialization method for them? If so, you could write an adapter that has a templated constructor that extracts all the information needed for serialization into a uniform representation. Then you visit the serializer with the adapter.
EDIT: After you added you code, I still think that an adapter could solve your problem, only the other way around. In you Accept-method, construct a local adapter and pass it to the Visitor. When the Visitor has modified it, you can use a template method extractToString on the adapter that converts the information to a specific string version. This may make the adapter quit complex, depending on how different the string-template instantiations have to be handled.
Since all your string classes are of different types, you will need some level of compromise (either a common sub-type, with virtual methods, for your strings, or an adapter, or adding a method for each different type to the visitor). Mixing generic-programming and oo can be a pain, especially if you don't accept compromises.
Eg.
class string_tag { /* common visitor interface */ };
template<typename char_t, ...> class basic_string : public string_tag {};
class IVisitor
{
public:
virtual void VisitString(string_tag& string) = 0; // this is what I want in theory
};
class MyObject
{
public:
typedef basic_string<char8, myAllocator, some_flag> MyStringType;
Accept(IVisitor* visitor)
{
visitor->VisitString(string);
}
private:
MyStringType string;
};
class MyOtherObject
{
public:
typedef basic_string<char16, myOtherAllocator, some_other_flag> MyOtherStringType;
Accept(IVisitor* visitor)
{
visitor->VisitString(string);
}
private:
MyOtherStringType string;
};
class Reader : public IVisitor
{
public:
virtual void VisitString(string_tag& string)
{
// read some data, give it to the string
}
}
May be you can consider below, but in this case you need to separate visitor mechanisms to different visitor classes. WStringVisitor and StringVisitor are just examples for different Visitor semantics.
#include <string>
#include <iostream>
using namespace std;
template <typename stringType>
class IVisitor{
public:
virtual void visit(stringType _string)=0;
};
class StringVisitor: public IVisitor<string>{
public:
void visit(string str){
cout<<"This is std::string implementation: "<< str << endl;
}
};
class WStringVisitor: public IVisitor<basic_string<wchar_t>>{
public:
void visit(basic_string<wchar_t> str){
//wprintf(L"This wide implementation : %S", str.c_str());
wcout<<"This is WString Visitor: "<< str << endl;
}
};
class MyObject{
public:
typedef basic_string<char> MyStringType;
void accept(IVisitor<MyStringType>& visitor){
visitor.visit("TEST STRING");
}
};
class MyOtherObject
{
public:
typedef basic_string<wchar_t> MyOtherStringType;
void accept(IVisitor<MyOtherStringType>& visitor)
{
visitor.visit(L"TEST WSTRING");
}
};
int _tmain(int argc, _TCHAR* argv[])
{
MyObject acceptor;
MyOtherObject otheracceptor;
StringVisitor visitor;
WStringVisitor wvisitor;
acceptor.accept(visitor);
//otheracceptor.accept(visitor); compile error
otheracceptor.accept(wvisitor);
return 0;
}
I think the fundamental problem here is that the Visitor pattern is all about virtual functions, while you herd your strings through function templates. And these just don't easily mix. In fact, the only way I can think of to mix the two is type erasure.
If you don't find a way to do what you want using this technique, I don't think you'll find a way.