473,657 Members | 2,530 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Design issue : "self type" as a default template argument (recursive template arguments)

IR
Hi,

I've been trying to do the following (which doesn't compile) :

template<class T, class F = Example<T
struct Example
{
F foo();
};

which gives me a "recursive type or function dependency context too
complex" error (I don't really understand _why_).

Trying to change the declaration to :

template<class T, class F = Example<T,F
struct Example
{
F foo();
};

just leads me to a "'F' : undeclared identifier" error (which I
fully understand, contrary to the previous error).

The goal I'm trying to achieve is to be able to write both

typedef Example<SomeCla ssSomeExample;

and

struct OtherExample : public Example<OtherCl ass,OtherExampl e>
{
//... whatever
};

SomeExample::fo o should have a return type of Example<SomeCla ss>
(aka. SomeExample due to the typedef), while OtherExample::f oo
should have a return type of OtherExample.

I've already done things like these, but without template default
argument, as the base template was then designed to be inherited
from (ctors protected, ...). But in this case, the base template
(Example) can be used "standalone ".
The scary part with this one, is that without Example's default
template argument (F), I wouldn't be able to write

typedef Example<SomeCla ss, Example<SomeCla ss, ... SomeExample;

due to the recursive nature of the declaration.
So I guess that I must really be doing something wrong.
The only solution I've come up with is something along the lines:

class SelfType {};

template<class T, class F = SelfType>
struct Example
{
private:
template<class Ustruct helper { typedef U Type; };
template<struct helper<SelfType { typedef Example<TType; };
typedef typename helper<F>::Type FinalType;
public:
FinalType foo();
};

This seems to work as far as I tested it, but I'm not satisfied
mainly due to the "scary part" I mentionned (which leads me to
believe I'm kinda playing with Evil).

Do you think I am missing something?
--
IR
Nov 21 '06 #1
3 2477
* IR:
Hi,

I've been trying to do the following (which doesn't compile) :

template<class T, class F = Example<T
struct Example
{
F foo();
};

which gives me a "recursive type or function dependency context too
complex" error (I don't really understand _why_).
Try do do the compiler's job. The definition of F is (filling in the
default) Example<T, F>, which is Example<T, Example<T, F, which is...
[snip]
typedef Example<SomeCla ssSomeExample;

and

struct OtherExample : public Example<OtherCl ass,OtherExampl e>
{
//... whatever
};

SomeExample::fo o should have a return type of Example<SomeCla ss>
(aka. SomeExample due to the typedef), while OtherExample::f oo
should have a return type of OtherExample.
So what's "OtherClass " used for, then?

One solution may be to split the single template definition into a
general one and a specialization.

However, with the vague specification (e.g. "OtherClass ") it's difficult
to give an example that wouldn't probably be misleading.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Nov 21 '06 #2
IR
Alf P. Steinbach wrote:
So what's "OtherClass " used for, then?

One solution may be to split the single template definition into a
general one and a specialization.

However, with the vague specification (e.g. "OtherClass ") it's
difficult to give an example that wouldn't probably be misleading.
Indeed, I guess I over-simplified the example. My intent was to
extract only the syntactic problem I was running into, but I lost
the global design on the way. So I'll try to be more precise...
I currently have a template class (Example in my previous post)
which acts as a specialized container of objects (T, SomeClass,
OtherClass, ...), using a std::map as its underlying implementation.
For simplicity, I'll pretend that it is a std::map<unsign ed,int>, and
get rid of the useless template parameters (policies).

It's member methods almost always return either a reference to itself,
or a modified copy. Most of these methods are symmetrical.
So it could be written:

template< /*original arguments ommitted*/ >
class Base
{
public:
Base();
Base(const Base&);
Base& operator =(const Base&);

Base& foo(int i)
{
// do whatever with i and map_
return *this;
};

Base bar(int i) const
{
Base tmp(*this);
tmp.foo(i);
return tmp;
};
private:
std::map<unsign ed,intmap_;
};

FYI foo/bar is actually about 20 pairs of symmetric overriden
functions (FinalType& fn1() / FinalType fn1() const, ...).
Due to the number of template arguments (6), this template class is
_always_ used through a typedef (which in reality often spans over 6-
10 lines, so without typedef the code would be unreadable):

typedef Base< /*...*/ Derived1;
typedef Base< /*...*/ Derived2;
etc...

So Derived1::bar returns a Derived1 (aka. Base</*...*/due to the
typedef), while Derived1::foo returns a Derived1&.
But I now feel the need to add an optional rule enforcer to some of
the derived classes (let's say to Derived2) so I thought I'd do the
following :

template</*...*/>
class Base
{
public:
Base(); // rules_ is initialized to NULL
Base(const Base&);
Base& operator =(const Base&);
~Base();

Base& foo(int i)
{
// do whatever with i and map_
if (rules_)
rules_->Enforce_foo(i, map_);
return *this;
};

Base bar(int i) const
{
Base tmp(*this);
tmp.foo(i);
return tmp;
};
protected:
struct RulesInterface
{
virtual void Enforce_foo(int , std::map<unsign ed,int>&) const = 0;
};
Base(const RulesInterface* rules)
// Initialize just like Base(), plus,
rules_(rules)
{
};
private:
std::map<unsign ed,intmap_;
const RulesInterface* rules_;
};

so I still can write
typedef Base</*...*/Derived1;

but also

class Derived2 : public Base</*...*/>
{
public:
Derived2() : Base</*...*/>(&myRules_), myRules_() {};
Derived2(const Derived2&);
Derived2& operator =(const Derived2&);
private:
struct MyRules : public Base</*...*/>::RulesInterfa ce
{
virtual void Enforce_foo(int i, std::map<unsign ed,int>& m) const
{
// do whatever with i and m
};
};
const MyRules myRules_;
};
A few things about the above code :

1) even though Derived2 passes a pointer to myRules_ while it isn't
yet constructed, it's (more or less) ok since Base doesn't refer to it
in it's ctors nor in it's dtor.

2) any Base derivative is "final" by design (it would make no sense to
derive again from DerivedN).

However the problem with that new version of Base/Derived2 is that the
existing codebase expects Derived2 to return Derived2 objects (or
Derived2& depending on which method is called).
Here I have two choices:

a) either override Base's methods in Derived2 so that they return
Derived2 instead of Base, which means code duplication = The Root Of
All Evil IMO, even worse than what's lying below...

b) or, tweak Base so that it can return a Derived2 object if needed
(which was the reason of my original post).
So now I have my Base template looking like this:

class SelfType{};

template</*... ,*/ class F = SelfType>
class Base
{
template<class U>
struct helper { typedef U Type; };
template<>
struct helper<SelfType { typedef Base</*...*/Type; };
typedef typename helper<F>::Type FinalType;
public:
Base(); // rules_ is initialized to NULL
Base(const Base&);
Base& operator =(const Base&);
~Base();

FinalType& foo(int i)
{
// do whatever with i and map_
if (rules_)
rules_->Enforce_foo(i, map_);
return static_cast<Fin alType&>(*this) ;
};

FinalType bar(int i) const
{
FinalType tmp(*this);
tmp.foo(i);
return tmp;
};
protected:
struct RulesInterface
{
virtual void Enforce_foo(int , std::map<unsign ed,int>&) const = 0;
};
Base(const RulesInterface* rules)
// Initialize just like Base(), plus,
rules_(rules)
{
};
private:
std::map<unsign ed,intmap_;
const RulesInterface* rules_;
};
The advantage of this solution is that it doesn't break any old code
(namely, typedef's with F defaulted to SelfType), while still allowing
me to write Derived2 just like I wrote it above, without additional
junk.

The disadvantage is that IMHO this implementation is plain ugly, so we
come to the very reason of my previous post: "do you think there is a
better design?"

I hope the simplified code I just posted isn't too messy, but there
might be a few typos left from the simplification of my original code.

--
IR
Nov 22 '06 #3
IR
Oh well, while double-checking my post for typos, one word I wrote
jumped to my face:

IR wrote:
...(policies).
So I figured out the solution: just add another policy as a template
parameter which default to a "do-nothing" policy...

struct UncheckedPolicy
{
static inline
void Enforce_foo(int i, std::map<unsign ed,int>& m) const
{
};
};
struct Derived2Policy
{
static /* inline? depending on the complexity */
void Enforce_foo(int i, std::map<unsign ed,int>& m) const
{
// do whatever with i and m
};
};
template< /*... ,*/ class Rules = UncheckedPolicy >
class Base
{
public:
Base();
Base(const Base&);
Base& operator =(const Base&);

Base& foo(int i)
{
// do whatever with i and map_
Rules::Enforce_ foo(i,map_);
return *this;
};

Base bar(int i) const
{
Base tmp(*this);
tmp.foo(i);
return tmp;
};
private:
std::map<unsign ed,intmap_;
};

typedef Base< /*...*/ Derived1;
typedef Base< /*... ,*/ Derived2Policy Derived2;
This solution has many performance / scalability / type safety /
readability advantages over my previous post, with no (obvious)
drawback.

I was indeed missing something! I can be so blind sometimes...
I'd still be glad to hear about what you think of both designs (my
previous post and this one).

Thanks for your attention anyway. :)

--
IR
Nov 22 '06 #4

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
2422
by: Todd Gardner | last post by:
Pardon my extremely ignorant newbie questions. Where can I go to find more information about the "self" argument? Is there something special about the word "self" or did Mr. Guido van Rossum just decide to us the word arbitrarily? More precisely it seems that most all classes have "self" as the first parameter. This may be directly obvious but is the word "self" always referring to the class or the function it is used in?
1
1627
by: Olaf Meding | last post by:
What does the below PyChecker warning mean? More importantly, is there a way to suppress it? PyChecker warning: ..\src\phaseid\integration.py:21: self is argument in staticmethod My best guess is that the warning is related to PyChecker not supporting C++ extensions.
2
3010
by: CoolPint | last post by:
Can anyone kindly explain why non-type template parameters are required by giving some examples where their uses are clearly favourable to other alternatives? I cannot think of any good use for them except to create different sizes of static arrays from a template, but this could be done by creating a dynamic array of different sizes accepting the sizes in the constructor thus not requiring the use of non-type template parameters and...
20
2858
by: Wayne Sutton | last post by:
OK, I'm a newbie... I'm trying to learn Python & have had fun with it so far. But I'm having trouble following the many code examples with the object "self." Can someone explain this usage in plain english? Thanks, Wayne
2
2589
by: Mark | last post by:
Visual C# .Net Standard 7.1.3088 I'm trying to change the type of application from a windows to a console app. I go into the project properties --> Application --> According to the help, I should have: Assembly Name Output Type Default Namespace Startup Project etc etc.
4
4031
by: Ondrej Spanel | last post by:
The code below does not compile with .NET 2003, I get folowing error: w:\c\Pokusy\delegTemplArg\delegTemplArg.cpp(11) : error C2993: 'float' : illegal type for non-type template parameter 'x' The error shows compiler is quite confused - x is not a template parameter at all. I have found two workarounds, but still I think the code should compile.
2
2869
by: Paul | last post by:
I am moving an existing app written years ago to a new server. It uses Sigma Template 1.3 and Quickform 1.1.1 and PEAR.php,v 1.1.1.1 2004/02/16 The directory structure is like this: /site /site/html/Pear.php /site/html/Sigma.php /site/html/Common.php /site/html/Quickform.php /site/html/Quickform/
13
12006
by: Kurda Yon | last post by:
Hi, I found one example which defines the addition of two vectors as a method of a class. It looks like that: class Vector: def __add__(self, other): data = for j in range(len(self.data)): data.append(self.data + other.data)
4
1729
by: danilo.turina | last post by:
Hi all, today I encountered a problem that I'm only able to solve by using reinterpret_cast (and I'd like to avoid it). This was my code until yesterday (omitting includes, "using namespace" and the like): ----------------------------------------- // ABC Table struct Table {
0
8421
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
8325
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
8844
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
8621
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
7354
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
5643
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4173
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
2
1971
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
2
1734
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.