Derive (virtual) function arguments in variadic template class - c++

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.

Related

How to iterate through all typenames in a class template?

I want to design a component-based weapon template for my game. However, it seems no way to add/remove a class member or create a code?
Sorry for my expression and lack of terminology, for I am not graduated from dept. of computer science or software engineer, I know little of what those stuff called by professionals.
Here is the component code looks like:
class CBaseWpnCmpt : public std::enable_shared_from_this<CBaseWpnCmpt>
{
public:
typedef std::shared_ptr<CBaseWpnCmpt> PTR;
private:
CBaseWpnCmpt() = default;
public:
CBaseWpnCmpt(const CBaseWpnCmpt& s) = default;
CBaseWpnCmpt(CBaseWpnCmpt&& s) = default;
CBaseWpnCmpt& operator=(const CBaseWpnCmpt& s) = default;
CBaseWpnCmpt& operator=(CBaseWpnCmpt&& s) = default;
virtual ~CBaseWpnCmpt() {}
protected:
CBaseWeaponInterface::PTR m_pWeapon { nullptr };
public:
template <class CComponent>
static std::shared_ptr<CComponent> Create(CBaseWeaponInterface::PTR pWeapon)
{
std::shared_ptr<CComponent> pComponent = std::make_shared<CComponent>();
pComponent->m_pWeapon = pWeapon;
return pComponent;
}
};
And this is what a weapon body code looks like: (And the problem occurs)
template < class CWeapon,
class ...CComponents
>
class CBaseWeaponTemplate : public CBaseWeaponInterface
{
public:
std::list<CBaseWpnCmpt::PTR> m_lstComponents;
public:
virtual void SecondaryAttack(void) // Example method.
{
for (auto& pComponent : m_rgpComponents)
{
pComponent->SecondaryAttack();
}
}
};
How am I suppose to create all these argument packs as member of the template? Currently I tried to enlist them into a pointer std::list container, but I just can't figure out how to achieve it at all.
In other words, how can I make a template when I fill in blank likt this:
class CAK47 : public CBaseWeaponTemplate<CAK47, CLongMagazine, CWoodenStock>
will generate this:
class CAK47
{
CLongMagazine m_comp1;
CWoodenStock m_comp2;
//... other stuff
};
Or alternatively, generate this:
class CAK47
{
CAK47() // constructor
{
for (/* somehow iterate through all typenames */)
{
CBaseWpnCmpt::PTR p = std::make_shared<typename>();
m_lstComponents.emplace_back(p);
}
}
};
One way of doing so from C++11 on-wards would be to store the template types used for this particular weapon inside an std::tuple
template <typename Weapon, typename... Attachments>
class WeaponWithAttachments {
protected:
WeaponWithAttachments() {
return;
}
std::tuple<Attachments...> attachment_types;
};
and then using that tuple to initialise a vector of shared pointers with a protected constructor taking a tuple to access the template types again.
class SomeWeaponWithAttachments: public WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment> {
public:
SomeWeaponWithAttachments()
: SomeWeaponWithAttachments{attachment_types} {
return;
}
protected:
template <typename... Attachments>
SomeWeaponWithAttachments(std::tuple<Attachments...> const&)
: attachments{std::make_shared<Attachments>()...} {
return;
}
std::vector<std::shared_ptr<BaseAttachment>> attachments;
};
Try it here!
If the attachments vector is already declared inside the parent class like it seems to be the case for you might also avoid the tuple and the protected constructor with initialising the attachments already inside the parent class
template <typename Weapon, typename... Attachments>
class WeaponWithAttachments {
protected:
WeaponWithAttachments()
: attachments{std::make_shared<Attachments>()...} {
return;
}
std::vector<std::shared_ptr<BaseAttachment>> attachments;
};
and then only calling the constructor of the base class in the derived class
class SomeWeaponWithAttachments: public WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment> {
public:
SomeWeaponWithAttachments()
: WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment>() {
return;
}
};
Try it here!
If that is no option for you, then you can use the tuple to iterate over all the template arguments using C++17 fold expressions:
class SomeWeaponWithAttachments: public WeaponWithAttachments<SomeWeapon,SomeAttachment,AnotherAttachment> {
public:
SomeWeaponWithAttachments()
: SomeWeaponWithAttachments{attachment_types} {
return;
}
protected:
template <typename... Attachments>
SomeWeaponWithAttachments(std::tuple<Attachments...> const&) {
(attachments.push_back(std::make_shared<Attachments>()), ...);
return;
}
};
Try it here!
In C++17 you might also add a static assertion with fold expressions into the constructor to make sure that the types actually inherit from BaseAttachment:
static_assert((std::is_base_of_v<BaseAttachment, Attachments> && ...), "Template arguments must inherit from 'BaseAttachment'.");

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.

Double partial template specialization for a class

I've stumbled upon a little problem with a little code I'm doing while learning c++11/14. Basically I have a Debugging class which I want to handle all the message printing. Most debugging/logging classes have log levels of sorts, but I want to use a flag for each message I have.
For that I have a little enum where I define my flags and their values:
enum DebugFlag {
Flag1 = 0,
Flag2 = 1,
Flag3 = 2
};
Aditionally, I have a Debugging class, which I've managed to specialize for Flag types and it works pretty well.
template<DebugFlag T>
class Debug {
public:
template <typename U>
static void print(U &&arg) {}
};
template <>
class Debug<static_cast<DebugFlag>(1)> {
public:
static void print(std::string &&message) {
std::cerr<<message<<"\n";
}
static void print(std::ostream &message) {
std::cerr<<DebugStream()().str()<<"\n";
DebugStream()().str("");
DebugStream()().clear();
}
static void print(std::string &message) {
std::cerr<<message<<"\n";
}
};
To call this class, I use a call like:
Debug<Flag1>::print("Message\n"); // should not do anything with Flag1 compiled to 0 value
Debug<Flag2>::print("Message\n"); // should work
Now I wanted to expand this class to also take bool values, so calls like this will work:
Debug< Flag2<2 >::print("Message\n"); // should not do anything with Flag1 compiled to 2 value
Debug< Flag2<1 >::print("Message\n"); // should work
The problem is I need a second partial specialization for my Debug class, that is bool, and I can't figure exactly what the syntax is for this.
This is the closest I've come to it, but still can't figure out what I'm doing wrong or if it's possible without making a secondary class and changing the way I want my call to look like: http://cpp.sh/6yemn
I don't understand exactly how you want to be able to use your class, but here's something that works.
template <typename T, T v = T()>
class Debug {};
template <>
class Debug<Flag, Flag2> {
public:
void f() { std::cout<<"This is good\n"; }
};
template <>
class Debug<bool, true> {
public:
void f() { std::cout<<"This is good too\n"; }
};
The problem is that you need to specify the type : whether you want to use a bool or a Flag, and then the value. You can instantiate the class like so :
Debug<bool, true> trueDebug;
Debug<Flag, Flag2> flag2Debug;
Other instances won't have the f function unless you add a specialization. For example :
template <Flag v>
class Debug<Flag, v> {
public:
void f() { std::cout<<"This is bad\n"; }
};
Live example

can't initialize functor objects when passing derived class in C++

This question stems from a previous question I asked here. I cannot use any external libraries or the C++ 11 spec. Meaning I can't use std::bind, std::function, boost::bind,boost::function etc. I have to write it myself. The issue is the following:
Consider the code:
EDIT
Here is a complete program that exhibits the problem as requested:
#include <map>
#include <iostream>
class Command {
public:
virtual void executeCommand() = 0;
};
class Functor {
public:
virtual Command * operator()()=0;
};
template <class T> class Function : public Functor {
private:
Command * (T::*fptr);
T* obj;
public:
Function(T* obj, Command * (T::*fptr)()):obj(obj),
fptr(fptr) {}
virtual Command * operator()(){
(*obj.*fptr)();
}
};
class Addition:public Command {
public:
virtual void executeCommand(){
int x;
int y;
x + y;
}
};
class CommandFactory {
public:
virtual Addition * createAdditionCommand() = 0;
};
class StackCommandFactory: public CommandFactory {
private:
Addition * add;
public:
StackCommandFactory():add(new Addition()) {}
virtual Addition * createAdditionCommand(){
return add;
}
};
void Foo(CommandFactory & fact) {
Function<CommandFactory> bar(&fact,&CommandFactory::createAdditionCommand);
}
int main() {
StackCommandFactory fact;
Foo(fact);
return 0;
}
The error it gives is "no instance of constructor "Function<T>::Function [with T=CommandFactory] matches the argument list, argument types are: (CommandFactory *, Addition * (CommandFactory::*)())
I think it's complaining because I'm passing it a derived type. I have to use pointers/references to the abstract classes because fact may not be a StackCommandFactory later down the road.
I can't say:
void Foo(CommandFactory & fact){
Function<CommandFactory> spf(&fact,&fact.createAdditionCommand); //error C2276
}
because of then I receive error C2276 which says (as in the question I linked to) '&' : illegal operation on bound member function expression.
So explicitly my question is: "How do I initialize this functor object so that I can use it with the above mentioned interfaces?"
Here's a modification of my original answer that seems to do what you need, without using the any functor stuff from C++11 or boost.
#include <vector>
#include <map>
#include <string>
struct Command {};
struct Subtract : Command {};
struct Add : Command {};
class CommandFactory
{
public:
virtual Subtract * createSubtractionCommand() = 0;
virtual Add * createAdditionCommand() = 0;
};
class StackCommandFactory : public CommandFactory
{
public:
virtual Subtract * createSubtractionCommand(void);
virtual Add * createAdditionCommand(void);
Subtract * sub;
Add * add;
};
Subtract * StackCommandFactory::createSubtractionCommand(void) { return sub; }
Add * StackCommandFactory::createAdditionCommand(void) { return add; }
class CommandGetterImpl
{
public:
virtual CommandGetterImpl* clone() const=0;
virtual Command* get()=0;
virtual ~CommandGetterImpl() {};
};
class CommandGetter
{
public:
Command* get() { return impl_->get(); }
~CommandGetter() { delete impl_; }
CommandGetter( const CommandGetter & other ) : impl_(other.impl_?other.impl_->clone():NULL) {}
CommandGetter& operator=( const CommandGetter & other ) {
if (&other!=this) impl_= other.impl_?other.impl_->clone():NULL;
return *this;
}
CommandGetter() : impl_(NULL) {}
CommandGetter( CommandGetterImpl * impl ) : impl_(impl) {}
CommandGetterImpl * impl_;
};
class Parser
{
public:
Parser (CommandFactory & fact);
std::map<std::string, CommandGetter > operations;
};
template<typename MEMFN, typename OBJ >
class MemFnCommandGetterImpl : public CommandGetterImpl
{
public:
MemFnCommandGetterImpl(MEMFN memfn, OBJ *obj) : memfn_(memfn), obj_(obj) {}
MemFnCommandGetterImpl* clone() const { return new MemFnCommandGetterImpl( memfn_, obj_) ; }
Command* get() { return (obj_->*memfn_)(); }
MEMFN memfn_;
OBJ * obj_;
};
template< typename MEMFN, typename OBJ >
CommandGetter my_bind( MEMFN memfn, OBJ * obj )
{
return CommandGetter( new MemFnCommandGetterImpl<MEMFN,OBJ>(memfn,obj) );
};
Parser::Parser(CommandFactory & fact)
{
operations["+"] = my_bind(&CommandFactory::createAdditionCommand, &fact);
operations["-"] = my_bind(&CommandFactory::createSubtractionCommand, &fact);
}
#include <iostream>
int main()
{
Add add;
Subtract sub;
StackCommandFactory command_factory;
command_factory.add = &add;
command_factory.sub= ⊂
Parser parser(command_factory);
std::cout<<"&add = "<<&add<<std::endl;
std::cout<<"Add = " << parser.operations["+"].get() <<std::endl;
std::cout<<"&sub = "<<&sub<<std::endl;
std::cout<<"Sub = " << parser.operations["-"].get() <<std::endl;
return 0;
}
You need an explicit cast on the 2nd parameter of the bar instance:
Function<CommandFactory> bar(&fact,
reinterpretet_cast<Command *(CommandFactory::*)()>(&CommandFactory::createAdditionCommand));
Besides, you're missing parens for the method pointer attribute in Function:
Command * (T::*fptr)();
This error might have prevented you to find the solution above.
You are also missing the return keyword in the operator() there (a mistake that I often do because of my functional programming habits):
virtual Command * operator()(){
return (obj->*fptr)();
}
You can avoid the cast by making the return type a template parameter:
template <class T, typename D>
class Function : public Functor {
private:
D * (T::*fptr);
T* obj;
public:
Function(T* obj, D * (T::*fptr)()): obj(obj), fptr(fptr){}
virtual Command * operator()(){
return (obj->*fptr)();
}
};
void Foo(CommandFactory & fact){
Function<CommandFactory, Addition> bar(&fact, &CommandFactory::createAdditionCommand);
}
Note that I did not templatize Functor after all. While it seemed a good idea at first to me, it make things a bit more complex. If you wish to make Functor a template too, the return type will have to be exactly the same, you cannot use an inheritance relation between them, unless you make them both parameters of the Function template. As a rule of thumb, whenever you stumble on a template issue like that, remember that template are like C macros at the core, it's a rewriting mechanism, which will expand the template into real C++ types (functions or classes) separately. You can picture the problem that way:
template <typename T, typename D>
class Function : public Functor<D> { /* ... */ };
will be expanded to
class Function<CommandFactory, Addition> : public Functor<Addition> {
/* ... */
};
Functor<Addition> and Functor<Command> bears no relationship at all; these are two different classes.
If C++ template did carry the notion of bounded polymorphism (like in Java or C#), it could have perhaps been possible to write it in way close to your intent.
I recommend:
keeping the Functor a simple class, to make the code simpler to work with for the time being, and
if the need arises later on, trying to refactor a working version with that new feature.
Generally speaking, it's a bad idea to use member function pointers as opposed to std::function. More generally,
typedef std::function<void()> Command;
typedef std::function<Command()> Functor;
Really, there's absolutely no need whatsoever for any member function pointers in your code.

Specify a base classes template parameters while instantiating a derived class?

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!