Inner class inside outer template class in C++17 - c++

I have a class template containing an inner class. I want the outer class to be the only one able to construct objects of the inner class but I need to expose the inner class to the outside so others can call a public method of its own.
Outer also defines a public interface for callback actions, in which it is supposed to provide an object of type Inner to the callee. So Outer must construct an Inner object and pass it as parameter in the callback method.
For example:
template<typename T>
class Outer {
public:
virtual void callbackMethod(std::shared_ptr<Outer<T>::Inner> inner) = 0;
class Inner {
private:
Obj obj;
Inner(...) {} // init obj, a private member of another type
std::shared_ptr<Inner> createInner() {
std::shared_ptr<Inner> inner = std::make_shared<Inner>(...);
... /* do some work */ ...
return inner;
}
public:
~Inner=default();
void exposedMethod() {
...
}
}
}
Under certain conditions, Outer creates an Inner object and calls the callback method, which is supposed to be implemented by someone else. The callee should then be able to do something like this:
inner->exposedMethod();
Of course, the idea is not letting outsiders to be able to construct Inner objects; this is why I designed Inner as a nested class and declared its constructor and creator methods as private.
My problem is that I need my Outer class to call createInner but the compiler complains about Outer trying to access private Inner constructor. I tried to declare Outer as friend inside Inner, but the error is still there:
class Inner {
friend class Outer<T>;
...
}
I have read that friendship follows other rules when applied to class templates. I am using clang with C++17.
So the question is, how could I design my classes so that class Outer can be the only one allowed to create objects of Inner type, it but at the same time be able to expose one of its methods to the outside? Please remember that Outer is a class template.
By the way, I don't care if the solution requires Inner to be a first-level class.
Thanks a lot.

A straightforward solution that doesn't require friending make_shared is lock-and-key:
template<typename T>
class Outer {
struct Inner_key{ explicit Inner_key() = default; };
public:
virtual void callbackMethod(std::shared_ptr<Outer<T>::Inner> inner) = 0;
class Inner {
private:
Obj obj;
public:
Inner(Inner_key, ...) {} // init obj, a private member of another type
std::shared_ptr<Inner> createInner(Inner_key key) {
std::shared_ptr<Inner> inner = std::make_shared<Inner>(key, ...);
... /* do some work */ ...
return inner;
}
~Inner=default();
void exposedMethod() {
...
}
}
}
Now anywhere in the program (including std::make_shared) can lookup Inner::createInner and Inner::Inner, but they can't be called without creating an Inner_key instance, which being a private nested type, only Outer member functions can do.

Related

How can I easily redirect a Base method to an identical Derived class method?

In working with a framework (Godot) that uses a register_method(<name>, <pointer_to_method>) to register a c++ method to a scripting API.
this method however doesn't support pointer to template classes.
So in the example:
static void _register_methods() {
register_method("my_method", &TMyClass::my_method); // this fails
// ^ template class
register_method("my_method", &MyClass::my_method); // this works
// ^ normal class
}
I have a template class TExample and an Example that extends the template class. The methods declarations and method definitions are all inside the TExample (however the methods are registered in Example).
So when I do:
register_method("my_method", &Example::my_method); // this fails because it is referencing the method of the parent class (template).
What I've found that works is redirecting the methods to "local" methods.
class Example : TExample<...>
{
public:
void my_method() {
TExample::my_method();
}
static void _register_methods() {
register_method("my_method", &Example::my_method); // this works
}
}
But imagine I have like 50 methods every time I want to create a new class from the template I need to redirect 50 methods. is there a shortcut to do this?!
Not sure what you mean by "this fails".
It just works, look (live demo):
template<class T>
class TExample {
public:
void my_method() {}
};
class Example : TExample<int> {
template<class U>
static void register_method(U u) {
}
public:
static void register_methods() {
register_method(&Example::my_method); // it works
register_method(&TExample::my_method); // this also works
}
};
int main()
{
Example ex;
ex.register_methods();
}
Now if you want to access my_method() from outside the class, then you should inherit publicly:
class Example : public TExample<...>
{
Then Example::my_method() will also work outside.
Note: TExample is not a template class, but a class template. However, in the context of a template instantiation (inside the definition of Example) the template arguments are substituted automatically.
Since template classes will be created for the types you are using you should mention the type also.
register_method("my_method", &TMyClass<Type>::my_method);
What about using a lambda ?
Does:
register_method("my_method", [&obj](*whateverParam*) { obj.templateMethod(*whateverParam*); } );
work?
(assuming obj contains the actual method, but that could be replaced by any instance containing the method).

Can initialization of a template class member be delegated to client?

I have faced with the following problem: how to initialize static Proxy which holds object that is provided as template parameter.
Here is my code snippet which shows the situation:
template<class SharedState>
class TToolbar : public ImplCustomToolBar
{
public:
virtual const ICustomToolbarsSharedState* const GetSharedState() const { return &sharedState_.Get();}
private:
// intent: static constructor for static SharedState initialization
class SharedStateHolder
{
public:
SharedStateHolder()
{
ToolbarsSharedState_.Initialize();
}
const SharedState& Get() const { return ToolbarsSharedState_;}
private:
SharedState ToolbarsSharedState_;
};
static SharedStateHolder sharedState_;
};
// I need TToolbar::SharedStateHolder sharedState_;
As it is unknown what template parameter will be provided, I can't use explicit specialization of template for the static member initialization.
So the only solution I found: clients of TToolbar should do initialization themselfs. But I think that it is error prone solution.
Question: what weaknesses are in my solution and what is the proper way of dealing with such situation?
You can use a static local variable of a member function:
template<class SharedState>
class TToolbar : public ImplCustomToolBar
{
virtual const ICustomToolbarsSharedState* const GetSharedState() const
{
static SharedStateHolder sharedState_;
return &sharedState_.Get();
}
};
The compiler is responsible for ensuring that the static is unique.
Or, if one wants to get rid of that Holder class:
template<class SharedState>
class TToolbar : public ImplCustomToolBar
{
virtual const ICustomToolbarsSharedState* const GetSharedState() const
{
static SharedState sharedState_;
static bool initialized = false;
if (!initialized)
{
sharedState_.Initialize();
initalized = true;
}
return &sharedState_;
}
};
One way to solve your problem is to delegate the construction of TToolBar to a factory, which knows about that shared state (basically, that's what SharedStateHolder does), and pass it to a protected constructor of TToolBar, which only that factory may use, by being friend with TToolBar. Objects needing such toolbar would have to go through the factory to get one.
template<typename SharedState>
class TToolBar {
friend class TToolBarFactory;
// ...
protected:
TToolBar(SharedState *shared){
// ...
}
// all other constructor also protected
};
class TToolBarFactory{
public:
TToolBarFactory();
TToolBar<SharedStateImpl> *makeToolBar();
// other toolbar pseudo constructors
};
Now SharedState construction is delegated to a dedicated class, which should be a singleton to ensure that toolbars all carry the same state.
Alternatively, since the SharedState instance is static, you can simply make a static method to set that variable once for all, and have the factory call it when it is instantiated. Then you would not need to pass it to TToolBar constructor (like I did above), and allow other objects to create them directly.
We may consider TToolBarFactory as a constructor for the TToolBar class, rather than its instances.
Note that this problem is orthogonal to the fact that TToolBar is parameterized on SharedState: you could make the factory directly provide specialized instances of TToolBar (like I did), or generalize it to a template, with a constructor a la std::vector::emplace, which is templated on the contained data constructor parameters.
template <typename SharedState>
class TToolBarFactory {
public:
template <class... args>
TToolBarFactory(Args...args){
TToolBar<SharedState>::setSharedState(new SharedState(args...));
// remaining factory init code.
}
// this could also be a variadic templated function
TToolBar<SharedState> *makeToolBar(){
return new TToolBar<SharedState>();
}
};
// example
class SharedStateImpl {
public:
SharedStateImpl(int i, float f){}
};
// the factory instance
TToolBarFactory<SharedStateImpl> factory(42,3.14);
int main(){
TToolBar<SharedStateImpl> *toobar = factory.makeToolBar();
}
If I understood your problem, it is really about having SharedState constructed outside TToolBar, because.you don't want that class to make any assumpions about SharedState constructors. My solution is a bit heavy weight. You could definitely do the same by simply making SharedStateHolder a template parameter of TToolBar, and keep all the initialization details there.

C++ Access private member of nested class

The title might be a bit misleading. I have the following problem: I have a tree consisting of leaves and internal nodes. The user should be able to store any information in the leaves and the tree has some methods which get a set of user-defined values and need to access the corresponding leaves in constant time (not amortized).
I came up with the following idea but it does not work because unfortunately I cannot access private members of a nested class: The user creates the tree and also for each leaf an instance of UserElement which contains the user_defined value for the corresponding leaf. Once a method like doSomethingWithTheTree(list>) is called and the tree is built, the tree creates the corresponding leaves and saves it in the private field leaf. Whenever the user wants to call a method with some of the leaves corresponding to its user_defined values, he/she just has to call the method by giving the corresponding UserElements and the tree can retrieve the corresponding leaves in constant time.
class Tree {
public:
template <typename T>
class UserElement {
private:
T user_value;
tree_node* leaf; // this has to be private for
// everyone outside the class `Tree`
public:
T getInf() {
return user_value;
}
void setInf(T i) {
user_value = i;
}
};
void doSomethingWithTheTree(list<UserElement<T>> elements) {
...
// I want to be able to access elem.leaf for all elements
}
}
Technically, that's a nested class (declared within another class), not a subclass (which inherits from its superclass).
You can allow the Tree class to access its privates by making it a friend:
class UserElement {
friend class Tree;
// ...
};
or, for better encapsulation, you could restrict access only to the member function(s) that need it, although it gets a bit messy due to the need to declare things in the right order:
class Tree {
public:
// Declare this so we can declare the function
template <typename T> class UserElement;
// Declare this before defining `UserElement` so we can use it
// in the friend declaration
template <typename T>
void doSomethingWithTheTree(list<UserElement<T>> elements) {
elements.front().leaf;
}
template <typename T>
class UserElement {
// Finally, we can declare it a friend.
friend void Tree::doSomethingWithTheTree<T>(list<UserElement<T>>);
// ...
};
};
You may do
class Outer {
private: // maybe protected:
class Inner {
public:
....
};
};
or
class Outer {
public:
class Inner {
friend class Outer;
private:
....
};
};
You can declare class Tree a friend to UserElement<>, which would allow Tree to access all members of UserElement<>.

Call a outter class virtual function from inner class of a static object

Easier to show than explain in only words
class OutterBase{
protected:
virtual ItemList buildDefaultItemListForCatagory1();
class Inner{
ItemList catagory1, catagory2;
public:
void Inner(){ _initOnce(); }
private:
void _initOnce(){
/* want to be able call the virtual buildDefaultItemListForCatagory1 */
}
}
typedef Singleton<Inner> InnerClassType; //has static getInstance method to get Inner object
}
class OutterDerived: public OutterBase{
virtual ItemList buildDefaultItemListForCatagory1();
}
So that's the situation right now. I want to be able to call the virtual buildDefaultItemListForCatagory1 inside Inner::_initOnce function. Is there a way to do this.
If not possible, then I need to redesign this. I want to only have one instance of Inner class in OutterBase and have it available to any derived class. I also need it to be constructed depending on the virtual buildDefault* function. Can someone suggest an alternative good solution if the above is not achievable? Thanks.
You can.
A nested (inner) class is a member with all the access rights of a normal member and thus has access to the protected and private members of its outer class.
The question is: call it on which object?
You can pass the outter object to the inner class constructor and do something like this:
class OutterBase{
protected:
virtual ItemList buildDefaultItemListForCatagory1();
class Inner{
ItemList catagory1, catagory2;
OutterBase *outter;
public:
Inner(OutterBase *_outter){ outter=_outter;_initOnce(); }
private:
void _initOnce(){
outter->buildDefaultItemListForCatagory1();
}
}
typedef Singleton<Inner> InnerClassType; //has static getInstance method to get Inner object
}

how to call methods of defined classes from within template class methods

How should I call a method within a defined class from a template class method?
Below is my scenario -
Template Class
template <class T>
class TC {
void myTemplateMethod() {
T.myMethod(); //can I call like this ?
}
};
Defined Class
class tdef {
void myMethod() {
//does something
}
};
Main
int main() {
TC<tdef> tobj;
tobj.myTemplateMethod(); //can I call tdef.myMethod() like this?
}
Just to note, that I have debugged a code like this and have found that tdef.myMethod() does not work when called like this. Also are there any chances that some exceptions are not handled while calling tdef.myMethod() from within Template class method?
-Somnath
That's a non-static member function, so it can only be called on an instance. Templates don't change that fact.
T t;
t.myMethod();
or if the function were static:
T::myMethod();