2

Consider the following code snippet.

template <T>
MyPtr<T> CreateObject()
{
    // Do something here first...

    // return our new object
    return MyPtr<T>(new T());
}

class Foo
{
private:
    Foo() { }

public:
    static MyPtr<Foo> GetNewInstance() 
    {
        // ERROR: Foo is private...
        return CreateObject<Foo>();
    }
};

class Bar
{
public:
    Bar() { }
};

int main()
{
    MyPtr<Bar> bar = CreateObject<Bar>();

    return 0;
}

Without resorting to macro for CreateObject (I like the syntax of MyPtr<type> obj = CreateObject<type>(params)), is there a way to make the function CreateObject share the same context as the caller function, thus able to access private Foo c'tor? 'friend' is not what I'm looking for as it would mean anyone calling CreateObject would have access to private Foo c'tor, which is not what I want. Overloading the new operator wouldn't work either as it is imperative that a MyPtr is returned instead of just T* (by assigning T* to MyPtr assigns a type to the object that is required somewhere else).

I guess what I'm looking for is something in between a macro and a template function (syntax of a template function but gets expanded fully like a macro). It would be quite useful to have this feature in this particular case.

Zach Saw
  • 4,308
  • 3
  • 33
  • 49
  • Wah? This is exactly what `friend` is for. How would you create an object without having access to its constructor? – Xeo Jul 05 '11 at 07:08
  • What keeps you from implementing `Foo::GetNewInstance()` as `return new MyPtr(new Foo())`?. I can't see that `CreateObject()` makes much sence here, really. – larsmoa Jul 05 '11 at 07:11
  • Like I said in the OP, I don't want caller of CreateObject to be able to access Foo unless it's within Foo itself... – Zach Saw Jul 05 '11 at 07:15
  • @lasrm: The sample code is obviously just there to illustrate the problem I'm facing. – Zach Saw Jul 05 '11 at 07:16
  • @Xeo: Like I said, it would be quite useful to have this feature in the standard -- as explained by my OP. – Zach Saw Jul 05 '11 at 07:25

5 Answers5

4

Well, you could do that with the passkey pattern:

template<class T, class PassKey>
MyPtr<T> CreateObject(PassKey const& key)
{
  return new T(key);
}

class FooKey{
private:
  FooKey(){} // private ctor
  FooKey(const FooKey&); // undefined private copy ctor

  friend class Foo;
};

class Foo{
public:
  // public ctor
  Foo(FooKey const&){}

  static MyPtr<Foo> GetNewInstance() 
  {
    return CreateObject<Foo>(FooKey());
  }
};

Example at Ideone.

With C++0x, this can be done much easier than creating a new Key struct every time, since template parameters are now allowed to be friends:

template<class T>
struct PassKey{
private:
  PassKey(){}
  PassKey(const PassKey<T>&);

  friend T;
};
Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • @Zach: And you can have an overloaded version of `CreateObject` that doesn't take a key, for classes that are publicly creatable. – Xeo Jul 05 '11 at 07:45
  • In non C++bleedingEdge you could use macros to similar effect, though perhaps your coworkers would crucify you. – Chris Lutz Jul 05 '11 at 07:46
  • @Chris: For what part exactly? If you mean for the creation of the structs, well, use macros where macros are useful. :P – Xeo Jul 05 '11 at 07:47
  • @Xeo - That's what I meant. I think it's a valid use of macros, but some people are unreasonably allergic to macros. – Chris Lutz Jul 05 '11 at 19:04
3

This is basically the same as attempting to use make_shared with a private constructor.

The only way to allow this is with friend. You're pretty much stuck in this case I'm afraid.

Community
  • 1
  • 1
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • Another restriction in the C++ language. Oh well, just as I thought. – Zach Saw Jul 05 '11 at 07:10
  • @Zach Saw - I believe you can make just the one instantiation of the function a `friend`. – Chris Lutz Jul 05 '11 at 07:13
  • Seeing that there are obviously genuine use-cases that require this feature, is the C++ committee aware of this shortcoming? – Zach Saw Jul 05 '11 at 07:13
  • @Chris Lutz: Care to elaborate? Like I said in the OP, I don't want caller of CreateObject to be able to access Foo unless it's within Foo itself... – Zach Saw Jul 05 '11 at 07:14
  • @Zach: I think this kind of thing is exceedingly rare, and I don't see how it could really be worked around. C++ already gets bashed quite often because it allows anything like `friend` in the first place. It's more than you get in pretty much every other OO language... – Billy ONeal Jul 05 '11 at 07:15
  • @Billy: Well, exceedingly rare, but I need it for my precise gc library. I have to agree that `friend` is easily abused but surely this is doesn't allow for such abuse... – Zach Saw Jul 05 '11 at 07:21
  • @Zach: I don't see a simple way to allow doing this, really. I think a generalized "I can only be called by functions A, B, and C" would cause more confusion than it would solve. I think anyone using your particular "precise GC library" would need to know enough about the library to not be calling `CreateObject` in the first place. Perhaps you should deny use of `CreateObject` and simply create a less public (e.g. in a `namespace detail`) version of `CreateObject` which is a `friend`, and which clients generally would not call. One must trust that clients aren't trying to be hostile. – Billy ONeal Jul 05 '11 at 07:25
  • @Billy: If that's the case, it would be easier to simply declare the c'tor public... I don't trust clients are trying to be hostile but I expect them to be sloppy. What I was thinking of was an addition to the C++ standard, a new keyword that asks the compiler to treat this template function to be in the same context as the caller. – Zach Saw Jul 05 '11 at 07:31
  • @Zach Saw - I'm not a C++ expert so it might not work but I believe `friend MyPtr CreateObject();` should make only the `Foo` specialization a friend, so that it can make the object but other template specializations can't. If that doesn't work you could specialize the `Foo` version of `CreateObject` (or `MyPtr`) yourself to work around the whole private constructor issue. – Chris Lutz Jul 05 '11 at 07:38
  • @Chris: What would prevent usages of CreateObject in client code though? – Zach Saw Jul 05 '11 at 07:42
  • @Zach - Forgive me if I'm slow to read or respond. I'm on my iPhone and my internet's being a bit slow, so I don't fully understand why you have `CreateObject` if you don't want people to call it. Why not just skip `CreateObject` inside `GetNewInstance` and make the `MyPtr` through some other method? – Chris Lutz Jul 05 '11 at 07:54
  • @Billy: I'm awarding the answer to you because boost::make_shared is essentially the same as my implementation now that I've taken a look at the boost source. – Zach Saw Jul 05 '11 at 08:05
1

I am not sure as to what you are trying to achieve. The simplification to post the problem here has taken away the actual need for the whole thing. So I will just assume that you know what you are doing, and that you really need this (and I suggest that you rethink whether you do need it, as I don't see a point...)

At any rate, you can solve the problem by passing a creator callback to the CreateObject template:

template <typename T, typename Creator>
MyPtr<T> CreateObject( Creator creator )
{
    // Do something here first...
    return MyPtr<T>(creator());
}
class Foo
{
private:
    Foo() {}
    static Foo* create() { return new Foo(); }
public:
    static MyPtr<Foo> GetNewInstance() {
        return CreateObject<Foo>( &Foo:create );
    }
// ...
};

The actual issue though, is what does Do something here first actually does that forces you into this complex creation patterns. The fact that it has to be executed before the creation of the new object seems to indicate that there are hidden dependencies not shown in the code, and that usually end up in maintenance nightmares, where someone down the line reorders some code, or adds a new constructor and everything seems to fall apart. Revisit your design and consider whether those dependencies can be simplified or made explicit.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • It's something like boost::make_shared, except it's make gc object in my case. – Zach Saw Jul 05 '11 at 08:03
  • It wouldn't be any more of a maintenance nightmare than Boost. – Zach Saw Jul 05 '11 at 08:26
  • @Zach Saw: They are not exactly the same. Nothing in boost requires you to execute code prior to calling the `new` operator. You can use `make_shared`, but you can also allocate in the stack or dynamically into a raw pointer or even a `shared_ptr`. Also, the code in `make_shared` does show the dependency, the object is built in place with placement new over a piece of memory that is allocated *before* in the same function, which is something that is missing from your question. The dependency would be shown if you had done: `CreateObject() { /*...*/ return new( buffer ) T(); }`, for example – David Rodríguez - dribeas Jul 05 '11 at 08:38
  • There is an obvious dependency shown in the code above: you cannot use placement new before the buffer is allocated. Note that I have not said that your design is wrong, only that you should consider it, as with the information present in the question it did not seem required. That is what I was referring above with *the simplification to post the problem has taken away the actual need for the whole thing*. – David Rodríguez - dribeas Jul 05 '11 at 08:40
  • It's not quite as straight forward as that in my case. C++ Builder wouldn't allow placement new on TObjects (delphi derived objects), thus requiring me to workaround that by doing that extra something before `new T`; It's not exactly the same as make_shared, but extremely close. – Zach Saw Jul 05 '11 at 08:42
  • That's the point of CreateObject -- it hides away the dependencies. Clients never have to and shouldn't call new. – Zach Saw Jul 05 '11 at 08:45
0

Since you are newing up the object in the very end it really doesn't relate to your CreateObject function. So Change the function prototype to:

template <typename T>
MyPtr<T> CreateObject(T* const p)
{
  //...
  return MyPtr<T>(p);
}

Usage:

static MyPtr<Foo> GetNewInstance() 
{
  return CreateObject(new Foo());
}
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • isn't `Foo` constructor private? – Donotalo Jul 05 '11 at 07:12
  • @ZachSaw, your `// Do something ...` has nothing to do with the `new T()`. So you can pass it externally. See my edited post. – iammilind Jul 05 '11 at 07:15
  • @iammilind: It has to occur before new T() -- hence `// Do something here FIRST` – Zach Saw Jul 05 '11 at 07:23
  • @Zach, at least from your code, it appears that you do nothing which influences to `new T()` (it's simple allocation in the end). If you wrote a pseudo code in your question then my answer doesn't apply. Otherwise I have guided in right way. Also you can think in this way, `"what effect it will make whether you do allocation in the beginning of CreateObject() or end ?"` – iammilind Jul 05 '11 at 07:28
  • @iammilind: Well, if there's no effect, then I wouldn't even need CreateObject function in the first place... – Zach Saw Jul 05 '11 at 07:33
0

is there a way to make the function CreateObject share the same context as the caller function

Yes, pass the context you need as an argument (either as an argument to the template, or as an argument to the function).

In practice, move the new T call to a separate function (or struct template, as I chose to do here), like this:

// Dummy representation of your pointer type
template <typename T>
struct MyPtr
{
    MyPtr( T *p ) { }
};

// Default constructor template; may be specialized to not use "new" or so.
template <typename T>
struct Constructor
{
    static T *invoke() { return new T; }
};

// Needs to be a struct (or class) so 'C' can have a default value
template <typename T, typename C = Constructor<T> >
struct CreateObject
{
    MyPtr<T> operator()() {
        return MyPtr<T>( C::invoke() );
    }
};

class Foo
{
private:
    friend struct Constructor<Foo>;
    Foo() { }

public:
    static MyPtr<Foo> GetNewInstance() 
    {
        return CreateObject<Foo>()();
    }
};

If you want to handle different constructor signatures (read: if not all types T have the same constructor signature), you could also choose to not pass the Constructor as a template to the CreateObject struct, but instead use a function argument. That way, you could 'load' a Constructor like this:

// ...
static MyPtr<Foo> GetNewInstance() 
{
     Constructor<Foo> c( arg1, arg2, arg3 );
     return CreateObject<Foo>( c );
}
Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
  • 1
    So, now I can do `Foo* p = Constructor::invoke();`. – Xeo Jul 05 '11 at 07:48
  • @Xeo: Exactly; the `Constructor` factors the `new T` part out of the `CreateObject` function. – Frerich Raabe Jul 05 '11 at 10:15
  • Ahm, yes, and it was the goal to *disallow* what I wrote above outside of the `Foo::GetNewInstance` function. You didn't solve that, only moved the problem to another function. – Xeo Jul 05 '11 at 10:26