3

I'm trying to add to every class in my project an in-class alias for shared_ptr, like so:

class Foo {
/* ... */
public:
    using Ptr = std::shared_ptr<Foo>;
};

so that I can define shared pointers to Foo with the shorthand Foo::Ptr fooObjPtr;. Is there any method to create a macro that automatically adds the alias? Something like:

#define DEFINE_SHARED using Ptr = std::shared_ptr<__CLASS_NAME__>;

class Foo {
/* ... */
public:

    DEFINE_SHARED
};
  • why do you need this? With `auto` and `std::make_shared` this doesn't provide any actual gain. – Marek R Jan 12 '21 at 11:49
  • I'm having too much of a mess in functions definition with lots of shared pointers in the parameters – Nicola Lissandrini Jan 12 '21 at 11:58
  • 2
    As with many other programming languages: If you're having "a mess of something" in C++ there is most likely more than one way of "doing it right". Feel free to open new question(s) outlining your problem to receive some guidance from more experienced developers. – Joel Bodenmann Jan 12 '21 at 12:00
  • I'm just having to pass smart pointers quite often to class constructors and so, it wouldn't bother me if I was using raw pointers, I was just looking for a more compact notation than std::shared_ptr. Maybe there is another way to write my code more compactly but it would be too hard to explain in a single question – Nicola Lissandrini Jan 12 '21 at 12:13
  • @NicolaLissandrini Hm, trying to reduce and fix some messed up stuff with a new questionable approach (or an approach that doesn't focus on the actual issues) might not be the way to stay robustly for the future. Pollution of at least a public interface/public class portions with stuff that doesn't semantically belongs to it, could likely lead to new confusion and drawbacks in the future. – Secundi Jan 12 '21 at 12:32
  • @Secundi I need pointers mainly for polymorphism – Nicola Lissandrini Jan 12 '21 at 14:42
  • This hits the usual issue of getting the type of a class from within its own definition, which still has no solution. If, however, you can live with `Ptr` instead of `Foo::Ptr`, then a trivial type alias template would do. – Quentin Jan 12 '21 at 16:52

3 Answers3

5

A class template can do this:

template<typename T>
class FooBase
{
    public:
        using Ptr = std::shared_ptr<T>;
};

class Foo :
    public FooBase<Foo>
{
};

int main()
{
    Foo::Ptr x = std::make_shared<Foo>();
    
    std::cout << x << std::endl;
}

This should achieve what you're asking for without relying on any pre-processor features.

Note that depending on your use case you might want to add some syntactic sugar such as ensuring that FooBase::T is actually inheriting from FooBase. There are several solutions for that - look up CRTP as that is a common "issue" there.

Joel Bodenmann
  • 2,152
  • 2
  • 17
  • 44
  • Do you think this is sitll a good practice if I had to inherit Foo with a subclass? In that case the Subclass::Ptr would refer to the pointer of a parent object – Nicola Lissandrini Jan 12 '21 at 12:15
  • Your question does not provide enough information to come up with a solution for every possible scenario. My recommendation would be to take a step back and analyse why you even need/want this in the first place. Feel free creating new questions outlining your overall problem/design to gather some feedback from experienced programmers. – Joel Bodenmann Jan 13 '21 at 09:16
0

you can use the following macro:

#define DEFINE_SHARED(x) using Ptr = std::shared_ptr<x>;

and then use it like:

class Foo {
/* ... */
public:

    DEFINE_SHARED(Foo)
};

newbie
  • 415
  • 3
  • 11
-1
#define CreateClass(classname) class classname { \
    using Ptr = std::shared_ptr<classname>;

#define CreateStruct(classname) struct classname { \
    using Ptr = std::shared_ptr<classname>;

It's pretty ugly, but would do the trick.

Example:

class MyClass {
    using Ptr = std::shared_ptr<MyClass>;

    virtual ~MyClass() = default;

    /* .... */
};

// Equivalent to:

CreateClass(MyClass)

    virtual ~MyClass() = default;

    /* .... */

};

@EDIT

Expanding the macro a little bit, you can add base classes:

#define _ClassCreation1(classname) class classname { \
    using Ptr = std::shared_ptr<classname>;

#define  _ClassCreation2(classname, baseclasses) class classname : baseclasses { \
    using Ptr = std::shared_ptr<classname>;

#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)

#define _ONE_OR_TWO_ARGS_1(a) _ClassCreation1(a)
#define _ONE_OR_TWO_ARGS_2(a, b) _ClassCreation2(a,b)

#define __ONE_OR_TWO_ARGS(N, ...) _ONE_OR_TWO_ARGS_ ## N (__VA_ARGS__)
#define _ONE_OR_TWO_ARGS(N, ...) __ONE_OR_TWO_ARGS(N, __VA_ARGS__)

#define CreateClass(...) _ONE_OR_TWO_ARGS(NARG2(__VA_ARGS__), __VA_ARGS__)

(based on: https://stackoverflow.com/a/11763196/5589708)

So you would use it like:

CreateClass(MyClass)

    virtual ~MyClass() = default;

    /* .... */
};

CreateClass(MyClass2, public MyClass)

    virtual ~MyClass2() = default;

    /* .... */

};
Felipe
  • 55
  • 6
  • 2
    Not the downvoter, but unmatched brackets in macro invocations is generally when I draw the "what the fridge am I doing" line... – Quentin Jan 12 '21 at 16:42
  • As I said, It's pretty ugly... But works as expected haha – Felipe Jan 12 '21 at 21:29