[Zeek-Dev] Question about IntrusivePtr and incomplete types

Dominik Charousset dominik.charousset at corelight.com
Tue Dec 17 07:28:28 PST 2019


>>> Is there some way to use an IntrusivePtr in this case - or is is just
>>> not possible to use it in cases where it is not possible to use complete
>>> types?
>> 
>> You should be able to get away with forward declarations as long as you provide matching prototypes for Ref and Unref.
> 
> If I do that I get linking errors:
> 
>  "Ref(Expr*)", referenced from:
>      Expr::Ref() in IdentifierInfo.cc.o
>      Expr::Ref() in Expr.cc.o
>      Expr::Ref() in ID.cc.o
>      WhenStmt::Exec(Frame*, stmt_flow_type&) const in Stmt.cc.o
>      Expr::Ref() in Type.cc.o
>      Expr::Ref() in Val.cc.o
>      make_var(ID*, BroType*, init_class, Expr*, List<Attr*>*, decl_type, int) in Var.cc.o
>      ...
>  "Unref(Expr*)", referenced from:
>      zeekygen::IdentifierInfo::Redefinition::~Redefinition() in IdentifierInfo.cc.o
>      Attr::~Attr() in Attr.cc.o
>      UnaryExpr::~UnaryExpr() in Expr.cc.o
>      BinaryExpr::~BinaryExpr() in Expr.cc.o
>      CondExpr::~CondExpr() in Expr.cc.o
>      ScheduleExpr::~ScheduleExpr() in Expr.cc.o
>      CallExpr::~CallExpr() in Expr.cc.o
>      ...
> 
> Which makes sense, because Ref/Unref(Expr*) never exist - they only exist for BroObj*. I could implement a forward function that does nothing besides calling Ref(BroObj) in Expr.cc - but that seems… not really elegant/worth using anymore in this case. Unless I an declare them in another way that I am missing.

I agree, that extra bit of boilerplate is not very elegant. However, the compiler has no knowledge of the class hierarchy of Expr and thus doesn’t know that Expr* is convertible to BroObj*. Hence, we need the one extra layer of indirection via Ref/Unref overloads for Expr.

Implementing the functions like this should make the linker happy as well:

void Ref(Expr* ptr) { Ref(static_cast<BroObj*>(ptr); }
void Unref(Expr* ptr) { Unref(static_cast<BroObj*>(ptr); }

There is one alternative: The compiler only needs to see Ref/Unref overloads when *instantiating* any member function of IntrusivePtr. You probably have some inline constructor or destructor that causes the compiler to instantiate the template. Moving *all* member function that operate on the IntrusivePtr member out of the .h file should work as well.

For example:

```
struct foo;

struct foobar {
  foobar();
  ~foobar();
  IntrusivePtr<foo> f;
};
```

should compile, whereas

```
struct foo;

struct foobar {
  foobar();
  ~foobar() {}
  IntrusivePtr<foo> f;
};
```

won’t. In the latter case, providing the destructor inline causes the compiler to instantiate the destructor of `IntrusivePtr<foo>`, which relies on an Unref function it can’t find at this point.


More information about the Zeek-Dev mailing list