Jump to content
  • Advertisement
Sign in to follow this  
Timkin

handling user supplied functions in libraries...

This topic is 4575 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm looking for advice on the best way to handle the current situation... I'm writing a library (in this case a pathfinding library) for which there will be a need to execute a function external to the library and return structured results from it. I want to supply a class in the library that can be used for passing back the correct info to the library for use in its search. In my present application, I'm also writing the other set of functions (domain model evaluation functions) but I want this interface to be general so I can use it in other pathfinding problems in other domains where the evaluation functions will be different (but the search algorithms involved dictate the sort of information required from an evaluation, hence the structured communication). Does this make sense? What I want is advice on how best to handle plugging in external functions to a library. I've been playing around with passing functors and have a working implementation along those lines, but I wanted to get more qualified advice from those experienced with this problem as to the best way to handle it so as to avoid reconfiguration (or redesign) of the library in the future. Thanks for any and all help, Timkin

Share this post


Link to post
Share on other sites
Advertisement
Generally, I'd do this by providing a pure virtual function in a base class and implement it with different subclasses of all the base types:



class BasePathPosition
{
// An abstract spatial location
}

class BasePathNode : public BasePathPosition
{
// A linked position
std::vector<BasePathNode*> m_vpLinkedNodes;
std::vector<float> m_pfLinkValue; // Used for range/terrain difficulty

void linkTo(BasePathNode* pNode,float value);

}

class PathRoute
{
// A collection of nodes in order of traversal
std::vector<BasePathNode*> m_vpRoute; // In order start to finish
float m_fTotalValue; // Total value of the route

std::vector<BasePathNode*>::iterator m_itCurrentNode; // Update as entity moves
}

class BaseNodeMap
{
BasePathNode* m_pRootNode; // Typical hierarchical node map
virtual BasePathNode* findClosestNode(BasePathPosition* pPosition)=0;
}

class BasePathfinder
{
virtual PathRoute* findPath(BaseNodeMap* pMap,PathPosition* pStart,PathPosition* pEnd)=0;
}

class ConcretePosition : public BasePathPosition
{
float x,y,z;
};

class ConcreteNodeMap : public BaseNodeMap
{
// This takes a ConcretePathPosition and returns a ConcretePathNode!
BasePathNode* findClosestNode(BasePathPosition* pPosition);
}

class ConcretePathFinder
{
// Call this with Concrete Positions and NodeMap!
PathRoute* findPath(BaseNodeMap* pMap,PathPosition* pStart,PathPosition pEnd*);
}



You get the idea. You may want some runtime type checking to ensure things are valid in the findPath function for robustness.

Hope this helps

Edit: Of course you may want to specify a tolerance or maximum value for the route the pathfinding function actually comes up with - again, implement the interface in the base class.

Also because this only requires dynamic linking, it means it's possible to produce a closed-source project off this if you LGPL the code.

Share this post


Link to post
Share on other sites
Quote:
Original post by _winterdyne_
Generally, I'd do this by providing a pure virtual function in a base class and implement it with different subclasses of all the base types:


So your library consists of an exported (pure virtual) base class which you require the user to derive from (and use the derived classes) in order to perform the actual search?

I'll have to give this some thought as to how it would work with my current search classes... sounds like a reasonable idea though... will post something back shortly.

Thanks,

Timkin

Share this post


Link to post
Share on other sites
No - I also provide concrete implementations for common use cases (3d spatial nodes, 2d nodes). But if the need arises, the base class can be derived from to use the same interface.

You only really need to keep a pure virtual if you absolutely, definitely need to force the user of the class to do something specific - for most libraries, once you've sufficiently defined the use case surrounding your task you should just be able to provide the function as is (no virtuals in the top-level class).


Share this post


Link to post
Share on other sites
Upon consideration I don't think your suggestion fits with what I'm trying to achieve (although I could be wrong). The impression I got from your example was that the user is writing the member functions for ConcretePathFinder, which is the implementation of the search.

I already have a search algorithm implemented within a library. I wont to separate the search implementation from its application. The user should be able to instantiate a search object to perform a search in a domain the user specifies through definitions of state type, action type, cost type and an action evaluation function. Then, any given search is evaluated given a starting state, goal state and initial resources... but the user doesn't have to write any of the search implementation... just choose the type of search by the search class they instantiate.

This doesn't seem to fit with what your suggesting. If my interpretation of your example is in error, please correct me. Otherwise, can you see what I'm suggesting and propose a paradigm?

Thanks,

Timkin

Share this post


Link to post
Share on other sites
I think I see what you're saying. You're after RTTI then.
Use the base class to provide the common interface for all your concrete pathfinding implementations, which you bury in the library (as per my example). I was actually suggesting that you produce at least *some* concrete implementations (just the abstract base class isn't a very functional library!).

You can rely on your user to create the appropriate type of object for their requirements:



// User code:
ConcretePathFinder UserPathFinder;
ConcretePosition MyPosition;
ConcretePosition DesiredPosition;
PathRoute* pRoute;

pRoute = ConcretePathFinder.findPath(&MyPosition,&DesiredPosition)



Or if you expect your user to use different types of path in the same application, you could provide a single aggregated interface:



// Library code:
class AggregatePathFinder
{
ConcretePathFinderA PathFinderA;
ConcretePathFinderB PathFinderB;

PathRoute* findPath(ConcretePositionA* pStart,ConcretePositionA* pEnd)
{
return PathFinderA.getPath(pStart,pEnd);
}

PathRoute* findPath(ConcretePositionB* pStart,ConcretePositionB* pEnd)
{
return PathFinderB.getPath(pStart,pEnd);
}
}

// User code:
AggregatePathFinder PathFinder;
ConcretePositionA AStart,AEnd;
ConcretePositionB BStart,BEnd;
PathRoute* ARoute, BRoute;

// The aggregate uses the same semantics regardless of type, but types must match:

// The 'A' Type pathfinder returns a PathRoute populated with ptrs to A Type nodes.
ARoute = AggregatePathFinder.findPath(AStart,AEnd);

// The 'B' Type pathfinder returns a PathRoute populated with ptrs to B Type nodes.
BRoute = AggregatePathFinder.findPath(BStart,BEnd);



If you're after a user being able to insert their own types into the aggregate, this is best handled with a means of RTTI, and have the aggregate check the incoming types to ensure they match, and then call an appropriate pathfinder held in a vector - this means you need to implement RTTI at the abstract level, as pure virtual 'int getType()=0' functions, although I tend to use strings for RTTI as it's much easier to see whats going on when debugging.

Share this post


Link to post
Share on other sites
Thanks for the advice _winterdyne_... I'll take a deeper look at it today when I get to work.

Cheers,

Timkin

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!