[C++] Passing template-less host class

Started by
20 comments, last by zyrolasting 14 years, 1 month ago
Quote:Original post by zyrolasting
Nullsquared had the idea closest to what I was looking for, although I admit I don't fully understand how to implement it. It sounds like a modified Pimpl.


Basically, something like this:
// declare an interface for bar'sstruct barBase{    virtual void policy1() = 0;    virtual void policy2() = 0;    // etc.};void doStuff(barBase &b){    b.policy1();}// a specific type of bartemplate<typename A, typename B>struct bar: barBase{    // implement the functions};doStuff(bar<int, int>()); // call doStuff with a specific type of bar


If you need the return types to be different between barBase and a class that implements barBase, look into covariant return types: http://en.wikipedia.org/wiki/Covariant_return_type

If you need the parameter types to be different between barBase and a class that implements barBase, you can either use void* or boost::any as the parameters, since you'll know what you have to cast to inside the class that implements barBase. I personally suggest using boost::any since it retains type info over void*, but if you're mostly using pointers and wish to avoid boost::any, then void* is fine.
Advertisement
Quote:Original post by zyrolasting
cache_hit, I mentioned that I do not want to use template templates twice now, and you ignored me on both occasions. Thanks, but no thanks. However, I do like that snippet you posted. I'll remember that for future decisions.

If my semantics are impossible, then I'll just toy with what I have to try and get as close to my goal as I can. I appreciate all of the suggestions I have been given, and my thanks go out to all of you.

Back to the drawing board...


First of all, your original post says "I wish to abandon dynamic binding". Ok, so you need static binding then. In C++ that is called "templates". Then, you gave an example specifically using templates that looks like something you want but isn't quite there. Well like it or not, template-templates are the only solution. Me mentioning that doesn't mean I'm ignoring you, it means I'm telling you that it's the only solution, and you should either find a way to make it fit in what you need, or bend a little on your requirements.

I could have posted alternate solutions that used dynamic binding that looked different, but I took "I wish to abandon dynamic binding" at face value. If you don't mind having your system dependent on dynamic binding, even if the interface to such dynamic binding is simplified somewhat, then I might have come up with a different answer.

Secondly, I did not ignore you, I responded very specifically to the reason you claimed you don't want to use them, and I provided a precise solution to that problem.

Quote:
The problem is, I have to tack template-templates onto the objects I'm passing it to. I don't want to do that. The resulting code would become confusing (picture 10+ template templates following you to every working object of your library)


I proposed a direct solution to that EXACT problem so that you can simplify the issue of passing template templates everywhere to the point where it's not confusing. If you still don't want to use them then you're more than welcome to settle for a solution that doesn't really fit your needs. Unless you actually don't mind using dynamic binding after all, in which case you can probably find a wealth of satisfactory solutions.
Quote:Original post by cache_hit
Well like it or not, template-templates are the only solution.


Considering how vague his question is, I doubt it's the only solution.
@cache_hit

Please don't steer us into one of "those threads". Once an argument starts here, we'll never get back on track. The bottom line is that I'm inexperienced in generic programming, and writing an essay to validate your solution that I already knew of and did not want to use does not move anything forward. Besides, if what you had really solved my problem, I wouldn't be defending myself right now. I accept I used "dynamic binding" in a contradictory manner. Sorry, now can we please drop this?

Quote:Considering how vague his question is, I doubt it's the only solution.


Sorry about that. What part of my question is most vague?
of course, apologies for steering it into the wrong direction. I may look again later and think about a different way to approach the problem.
Quote:Original post by zyrolasting
Sorry about that. What part of my question is most vague?


The policies part; I don't quite understand how you use them (perhaps this is an issue on my part). Maybe a small code example?

Based on the code example you gave in your OP, my idea should work; did you take a look at it?
So zyrolasting, it looks like you just want to declare Func with an argument that needs no template arguments, not abandon dynamic binding completely. In that case, nullSquared has the best solution it seems to me. Or just use boost::any.
Quote:The policies part; I don't quite understand how you use them (perhaps this is an issue on my part). Maybe a small code example?


Sure, here is the simplest pseudo-code doppelganger to my actual project I can think of. Read the comments for tidbits.

/*NOTE: This is contextual!! I want to write a whole SLEW ofstuff like this the user can select fromto build an enumeration object easily.*/struct FormatRanker{	static int Rank( const Format& f )	{		// Accept 32 bit format.		return ( f.GetBitCount() == 32 );	}};/*This holds accepted features.One policy is needed here that exposesa Rank() method that returns int and acceptsa const reference of type T. (See above)*/template <class T, class RankPolicy>class container{	storage mStorage;public:	bool Add( const T& feat )	{		if ( RankPolicy::Rank(feat) < 0 )		{			// Reject			return false;		}		// Accept		mStorage.Add(feat);		return true;	}};// This collects core information.template<class RankDisplayPolicy>struct HiEnumerator{	container<Display,RankDisplayPolicy> m_Container;	Enumerate()        {            while ( !DoneEnumeratingDisplays() )            {                Display = GetNextSupportedDisplay();                m_Container.Add(Display);            }        }};/*The below enumerator needs informationfrom the above one.This needs to know the template argumentsof HiEnumerator, but it won't use them.Which brings us back to: How can I remove the belowtemplate argument list and still be aware of HiEnumerator?*/template<class DisplayPolicy> struct LoEnumerator{	Enumerate( HiEnumerator<DisplayPolicy>& )        {              // Get enumerated info from HiEnumerator,              // But don't care about it's policies.        }};


I felt nullsquared was on the right track because I needed to separate interface from implementation, but templates are indeed in the mix.

I certainly could use template templates, but I am enumerating Direct3D9 here... The actual template argument list is staggering. The code would be very difficult to read and maintain on my end. If I indeed had only one template argument to worry about and little to collect, I would never have started this thread.

Quote:Based on the code example you gave in your OP, my idea should work; did you take a look at it?


I'm actually thinking about it right now. Nothing is set in stone yet, though.

Hopefully the above cleared up any confusion.
If the project is not confidential, would it be possible to instead describe what the result is supposed to be, or what the user will do?

The best I can guess:
- Enumerate D3D display modes
- Allow user (end user? developer? via config file?) filter that list for some purpose?

Because I do not know what templates have to do with any of this, or what actually needs to be a templated type, and what is concrete.

Long time ago I wrote a display filtering class which used EnumDisplaySettings to put all modes into vector<DEVMODE> (DEVMODE is plain C struct). Then I'd just iterate over that in a for loop. Generic version would be something like:
bool predicate(DEVMODE & dm); // whatever filter to applytemplate < class Pred >void visit(std::vector<DEVMODE> & in, std::vector<DEVMODE> &out, Pred p) {  out.clear();  for (int i = 0; i < in.size(); i++) {    if (p(in)) out.add(in));  }}


To combine multiple filters, one could just call this sequentially:
visit(a, b, ByResolution); a=b; b.clear();visit(a, b, ByDepth);  a=b; b.clear();visit(a, b, ByFoo);// b contains the result


My objects try to enumerate everything. The user may rank each feature found to be supported, or reject it altogether. This may need to be done once each project, so code may get very repetitious if I used virtual methods. They would be much easier than what I'm doing here, but I feel the use of policies play an important role in reducing that repetition and easing maintenance as a user.

Once the dust settles I would end up with a single run-of-the-mill host class that wraps all aforementioned logic I've shared above and expose a single long template parameter list. The only templated types are policies that rank an enumerated feature. This is why the large collection of pre-written policies was so appealing to me. Everything else should be concrete. I still wanted to compartmentalize the wrapped code and reduce the need of knowing about templates that aren't used, hence the thread...

The end result code might look something like:

typedef IDXEnum<DispModePolicy_43_169_Aspect, // Only accept 4:3 and 16:9 display modesFormatPolicy_32BitOnly, // 32 bit adapter formatsFormatPolicy_32BitOnly, // 32 bit back buffer formatsFormatPolicy_DepthAndStencil, // Need depth and stencil bitsCapsPolicy_HTNL_Shader_3 // HW T&L, vertex/pixel shader 3.0 required...> MyEnum;// Usage: D3D is an IDirect3D9*MyEnum e(D3D);// Use methods to create device with enumerated data, etc...

This topic is closed to new replies.

Advertisement