Conditionally defining method for container class - c++

Is it possible (and is it a good idea) to conditionally define methods for some container class (template<typename ThingType> class Container) depending on the type of its elements? I first thought it was possible after reading about std::enable_if, but now I am not certain I understand.
Below is my attempt (click here to run on ideone). In the case that std::is_base_of<ThingBase, ThingType>::value is false, a return type for p will not be defined. I figured the compiler would just instantiate the object of a class without that method. But it turns out it doesn't compile.
Is there another tool for the job? Or should I write two Container-like classes that have different behavior depending on what ThingType is? Or maybe this is a job for a specialization.
#include <iostream>
#include <type_traits>
#include <vector>
class ThingBase {
public:
virtual void printHi() = 0;
};
class Thing : public ThingBase
{
void printHi(){
std::cout << "hi\n";
}
};
template<typename ThingType>
class Container{
private:
std::vector<ThingType> m_things;
public:
typename std::enable_if<std::is_base_of<ThingBase, ThingType>::value>::type p()
{
m_things[0].printHi();
};
};
int main() {
//Container<Thing> stuff; // works!
Container<int> stuff; // doesn't work :(
return 0;
}
Edits:
The error message from the compiler is
prog.cpp: In instantiation of ‘class Container<int>’:
prog.cpp:36:17: required from here
prog.cpp:26:78: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
typename std::enable_if<std::is_base_of<ThingBase, ThingType>::value>::type p()
#StoryTeller - Unslander Monica I don't intend for this method to be overloaded. I want it to be available to the end user whenever it makes sense for it to be available. There will only be one of these p methods, and it should take only one (relatively simple) signature.

I don't intend for this method to be overloaded. I want it to be available to the end user whenever it makes sense for it to be available. There will only be one of these p methods, and it should take only one (relatively simple) signature.
This simplifies the exercise quite a bit. The solution is... to do nothing special.
void p() {
m_things[0].printHi();
}
When a class template is implicitly instantiated, only the declarations of member function are instantiated along with it. Definitions are not instantiated until an attempt is made to use the member.
So you do not need to do anything special. The error will happen if it's used when it cannot be used.
If still you wish to ensure derivability, and produce a descriptive error in that case, you can add a static_assert to the member function body. Simply use the is_base_of test for a condition, and add a nice string to accompany it.
This is a common approach when writing generic utilities. SFINAE's primary purpose on the other hand is to control overload resolution. But you aren't doing it here.

Related

this->field vs. this.field in C++

How do lines (2) and (3) even compile in the following C++ class, given that this is a pointer, so should need -> notation to access fields (as seen in line (1))? (Source)
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
template <typename T>
class sptr_wrapper
{
private:
boost::shared_ptr<T> sptr;
public:
template <typename ...ARGS>
explicit sptr_wrapper(ARGS... a)
{
this->sptr = boost::make_shared<T>(a...);
}
explicit sptr_wrapper(boost::shared_ptr<T> sptr)
{
this->sptr = sptr; // (1)
}
virtual ~sptr_wrapper() noexcept = default;
void set_from_sptr(boost::shared_ptr<T> sptr)
{
this.sptr = sptr; // (2)
}
boost::shared_ptr<T> get_sptr() const
{
return sptr; // (3)
}
};
The line (2) is invalid. As you said, this is a pointer, we need to use -> instead of .
As the member of class template, sptr_wrapper::set_from_sptr is not required to be instantiated, until it's used. So you can add some code trying to call it, then you might get compile-errors as you expect.
This applies to the members of the class template: unless the member is used in the program, it is not instantiated, and does not require a definition.
The line (3) is valid; sptr refers to the member sptr, which has the same effect as this->sptr.
When a non-static class member is used in any of the contexts where the this keyword is allowed (non-static member function bodies, member initializer lists, default member initializers), the implicit this-> is automatically added before the name, resulting in a member access expression (which, if the member is a virtual member function, results in a virtual function call).
Would you believe that the reason this compiles is because nothing really gets compiled here?
The shown code defines a template.
A template does not become "real" until it instantiates a class. Only at that time the compiler gets a closer look at the template, and attempts to figure WTF it's doing.
Sure, when defining a template the compiler makes a half-hearted attempt to parse the template, but only barely enough to satisfy itself that the template consists of some plausibly-looking C++ code.
If you add some additional lines to the shown code you'll get the compilation errors you were yearning for:
class X {};
void foo()
{
sptr_wrapper<X> x;
boost::shared_ptr<X> y;
x.set_from_sptr(y);
}
And this produces the compilation errors you were looking for:
t.C:27:14: error: request for member ‘sptr’ in ‘(sptr_wrapper<X>*)this’, which is of pointer type ‘sptr_wrapper<X>*’ (maybe you meant to use ‘->’ ?)
27 | this.sptr = sptr; // (2)
Note that merely instantiating
sptr_wrapper<X> x;
isn't enough. You have to go full throttle and invoke the method in question, before it becomes "real" in the eyes of a C++ compiler, and it chokes on it.
It's true that I can quite think of any circumstance where "this.foo" might be valid C++ code, but I'm sure that somewhere in the 2000 pages that make up the current C++ standard, the exact details of what's going on gets spelled out in a very pedantic way.
And you might consider dropping a note to your compiler's bug tracker, a feature request to have your compiler issue a friendly warning, in advance, when it sees something like this.

CRTP - visibility of the type of a nested leaf class

I would like to understand if it is possible to use a nested class of a leaf CRTP class in the base CRTP class. The example below demonstrates the problem.
#include <iostream>
using namespace std;
template<class T>
class A
{
protected:
T* asLeaf(void)
{return static_cast<T*>(this);}
T const* asLeaf(void) const
{return static_cast<T const*>(this);}
public:
struct Inner
{int a = 10;};
void dispInner(void) const
{std::cout << asLeaf()->inner.a << std::endl;}
// I would like to use T::Inner in this class, e.g.
// typename T::Inner mvInnerA;
// However, I understand that it is not possible to
// use it in the form that is stated above. Thus,
// I am looking for any possible workarounds.
};
class B: public A<B>
{
public:
struct Inner: public A<B>::Inner
{int b = 20;};
protected:
friend A<B>;
B::Inner inner;
public:
void dispInner(void) const
{
A<B>::dispInner();
std::cout << asLeaf()->inner.b << std::endl;
}
};
int main()
{
B b;
b.dispInner();
return 0;
}
EDIT
I would like to provide several further comments based on the feedback that I have received:
I am aware that I may not be using adequate design practices. In particular, it may be questioned whether A should be aware of the existence of inner. However, I would like to define an object inner of the type B::Inner in A instead of providing the definition of inner in B and using it in A.
I am aware that I cannot forward declare B and/or B::Inner and of the reasons why this cannot be done. Thus, technically, the design problem does not have a solution. However, I am looking for a feasible workaround.
I have already considered several alternative solutions:
One of the possible feasible solutions is not to make attempts to 'define' B::Inner inner in A and use the member functions of A to provide the functionality that allows to modify the A<B>::Inner part of B::Inner inner.
Another possible solution is to define the classes A<B>::Inner and B::Inner explicitly (i.e. not as nested classes). However, I would prefer to avoid this, because, by design, it is not expected that any classes that do not derive from A will need to interact with A<B>::Inner or the classes that derive from A<B>::Inner
Both solutions that I have presented may be acceptable. However, I am looking for any feasible alternatives.
The standard say that:
A class is considered a completely-defined object type (or complete type) at the closing } of the class-specifier.
It follows that B isn't a completely-defined object when you specialize A as A<B>. Therefore you can't expect to be able to access its members or types or whatever from within the definition of A (even though you can call back the derived class from within the definition of a member method of A, that is perfectly legal instead other than the purpose of the CRTP idiom).
In other terms, when you do this:
typename T::Inner mvInnerA
You have no guarantees that T is a completely-defined object and that's why you get the error.
A few alternatives:
You can define mvInnerType as a function instead of as a type and use it as a factory to create objects of type T::inner:
[static] auto mvInnerA() {
return typename T::Inner{};
}
Use it either as:
auto foo = A<B>::mvInnerA();
Or:
auto foo = obj.mvInnerA();
The right form depends on the fact that you make it static or not.
Note that you can still use the hidden type somehow, even if its name isn't accessible:
using HiddenType = decltype(A<B>::mvInnerA());
HiddenType foo = A<B>::mvInnerA();
You can define mvInnerA using a template a alias declaration like this:
template<typename U = T>
using mvInnerA = typename U::Inner;
Then use it as:
auto foo = A<B>::mvInnerA<>{};
For the type T is (let me say) indirectly used through U only when mvInnerA is instantiated, you don't have the problem mentioned above. The price to pay for that is the presence of an annoying <> and the fact that one can pass a custom type to mvInnerA.
How you can use an inner type of a CRTP template parameter is severely limited.
There can be no use in the scope of the class template definition itself. When instantiating the template, it will require the type B to be fully defined, which like skypjack points out, it isn't. You can however use it in contexts that aren't immediately instantiated with the class template, which is mostly the member functions of A.
And while you cannot have a type alias for B::Inner, you can have a type alias template
template<class C>
using Inner = typename C::Inner
Which A's member functions can use to avoid the verbosity of typename B::Inner and instead use Inner<B>.

Call function of template class created at runtime

I have a tricky question about C++(11) template classes and their instantiation with types determined at runtime:
Following scenario:
The user defines the type of a template class using a config file (ROS parameters). This determines only the type of the template class, not the further logic:
Class definition:
template<typename T>
class MyClass {
//[...]
}
Exemplary code:
/* [Read parameter and write result to bool use_int] */
std::unique_ptr<MyClass> myclassptr {nullptr};
if(use_int) {
myclassptr.reset(MyClass<int>);
} else {
myclassptr.reset(MyClass<double>);
}
myclassptr->foobar();
/* [more code making use of myclassptr] */
So this code is (of course) not compiling, because the unique_ptr template must be specified also with the template type. However, then the problem arises that the template type must be the same for all objects assigned using reset.
One ugly solution would be to copy the code myclassptr->foobar(); and the following into each branch of if/else, which I really don't like.
I would like to see a solution similar to this:
/* [Read parameter and write result to bool use_int] */
MyClass<use_int ? int : double> myclass;
myclass.foobar();
What I have read so far is that something like this is also not possible.
Does anybody have a nice solution for this?
The simplest way to do this is:
class IClass{
virtual ~IClass {}
virtual void foobar()=0;
};
template<typename T>
class MyClass:public IClass {
public:
void foobar() override {
// code here
}
};
std::unique_ptr<IClass> myclassptr {};
if(use_int) {
myclassptr.reset(new MyClass<int>());
} else {
myclassptr.reset(new MyClass<double>());
}
myclassptr->foobar();
boost::variant would be another solution, but is usually used for unrelated types. Type erasure could be done, but again that is usually done when you have unrelated types you want to impose a uniform interface on.
In other languages generics look sort of like templates, but are actually an abstract interface with auto-generated typecasting and some typechecking added. C++ templates are function or class compile time factories. Two outputs of such factories are unrelated at runtime by default, and you can add such relations if you want.
Depending on what you want, you can make MyClass a variant type that holds either an int or a double, or you could use type erasure to hide the implementation behind an interface. The Boost.Variant library can help to implement the former.

Type aliases and incomplete types

I'm probably over reaching here to solve what should be a simple problem. I started this question here:
Getting type of base class at compile time
Basically I'm trying to make the class manage it's own pointer types. I'm wrapping a C library where some structures have reference counting embedded in them, and others do not. Those that don't, I'd like to use shared_ptr. Those that do, I'd like to use intrusive_ptr. I'd like to avoid relying on programmer intellect to ensure the use of the proper wrapper. Eventually, I'd like to add more capability that relies on this behavior, but I'm not there yet.
#Yakk came up with an interesting solution using template type aliases, and I've tried to implement it. Unfortunately I've got myself in a spot where I can't seem to resolve circular references to the compiler's satisfaction. I get an "incomplete type 'Test2' named in nested name specifier" error pointing to the "using pointer=" line. I also get a bizarre "definition differs from declaration in return type" for my definition of Test::f(), but I suspect that might resolve itself once I get the first error resolved.
Most of the references I find on this error type involve sequencing of header files, but even with everything in one file I can't figure out how to order things to make this problem go away.
Any ideas?
#include <iostream>
#include <memory>
template<typename T>
class Pointered: public std::enable_shared_from_this<T>
{
public:
using pointer=std::shared_ptr<T>; //<-- incomplete type named error
using weakPointer = std::weak_ptr<T>;
};
template<typename T>
using Ptr = typename T:: pointer;
template<typename T>
using WkPtr = typename T:: weakPointer;
class Test2;
class Test:public Pointered<Test>
{
public:
Ptr<Test2> f();
};
class Test2:public Pointered<Test2>
{
public:
Ptr<Test> p;
Test2(Ptr<Test> ptr):p(ptr){}
};
int main(int argc, const char * argv[])
{
Ptr<Test> p=std::make_shared<Test>();
Ptr<Test> p3=p;
p->f();
std::cout << "Refcount: " << p.use_count() << std::endl;
}
//definition differs from declaration in return type error here
Ptr<Test2> Test::f()
{
return Ptr<Test2>(new Test2((Ptr<Test>)shared_from_this()));
}
You cannot forward declare nested types, for that you need the definition of the enclosing type, but in your case you have a cyclic dependency that inhibits this.
The first thing to consider is whether the cyclic dependency is really a good idea. In most cases cyclic dependencies are considered a code smell (an indication of problems with the design). If you can remove the cyclic dependency, then everything will become easier.
A different alternative is moving the dependency on the nested type to a type-trait, that can be defined externally and before the definition of the types:
template <typename T>
struct pointer_traits;
template <>
struct pointer_traits<Test1> {
typedef std::shared_ptr<Test1> ptr;
typedef std::shared_ptr<Test1> wptr;
};
By moving the dependency outside of the real type, you no longer have the cyclic dependency (at the syntax level, you should still revisit the design). You can then add syntactic sugar as needed:
template <typename T>
using ptr = typename pointer_traits<T>::ptr;
class Test1 {
ptr<Test2> p2;
};
If you really want the types to also be nested, you can use inheritance to bring those into scope, or in a simpler way just add the appropriate typedef:
class Test1 {
typedef ptr<Test1> ptr_t;
// ...
Note, this is a rough approximation, if you opt for this approach you can work a bit on the details of the trait and the types to make it sweeter by adding more syntactic sugar, for example, you can provide two traits for shared_ptr_traits and intrusive_ptr_traits and provide a single line trait to determine from which of those you want to pull the typedefs, reducing the definition of the trait (per type) to a single line.

Generic class variable of a certain type

In C# I can define this:
public interface BaseObject
{
int GetValue();
}
public class Test<T> where T : BaseClass
{
T BaseObject;
}
which means I know that I can alwaysa call BaseObject.GetValue() / BaseObject->GetValue(); because I know that the baseobject has this method.
Is there a similiar way to do this in C++? So that I can define an interface that multiple classes can inherit and a class that can take advantage of this.
Templates, which are even more powerful than C# generics (not to say they are necessarily better, just different).
template<class T>
class foo
{
public:
int whatever()
{
return obj.GetValue();
}
private:
T obj;
};
A separate class is created for each template argument you use. If you provide a template type which would result in an error you will know at compile time.
You're asking about C++ concepts, a way to specify requirements for template parameters. They were proposed during the work on C++11, but proved complicated enough that they weren't done in time. But they've just been delayed, not forgotten.
In the meantime, duck typing remains very powerful, and it will catch when you pass a template parameter that doesn't have the required interface. It just won't report the problem as neatly.
As a workaround, a simple way to check the constraint you showed takes advantage of the fact that pointer conversions are implicit only when upcasting:
public class Test<T> where T : BaseClass
{
static T* enforcement_helper = 0;
static BaseClass* enforce_inheritance_constraint = enforcement_helper;
};
Depending on how new your compiler is, you may need to put those lines inside a special member function (destructor is good, because it's almost always processed).
But you should only check constraints in order to improve error messages (by causing the failure in a clearly commented section of code). C++ templates are duck typed, and they will work with any template parameters that provide the required operations. No formal "interface" is required.