recursive import

1,059 views
Skip to first unread message

ly.p...@gmail.com

unread,
May 28, 2009, 12:51:04 PM5/28/09
to Protocol Buffers
Hi,

i need a message A to have a field with a message B, and at the same
time i need messages B to have a field to message A, how to avoid
recursive imports ??

cheers,

Pascal Ly.

ly.p...@gmail.com

unread,
May 28, 2009, 1:16:38 PM5/28/09
to Protocol Buffers
Just to complete the view, i' m trying to simulate inheritance.

Derived Classes need to have a required field to their mother (so that
when i'm using directly a derived class i can set the super class
fields).

And abstract classes need to have an optional field
to all possible derived classes (emulate the fact that a super class
can become any sort of derived class).

I can bypass the problem by using "extend" in the derived classes
message file (extending field of the super class, adding an optional
field of themselves) , so that only the derived classes side need to
use import.

but i was wondering if there was another way to do so ??

cheers,

Pascal ly

Kenton Varda

unread,
May 28, 2009, 2:44:25 PM5/28/09
to ly.p...@gmail.com, Protocol Buffers
You can only have mutually-recursive messages if they are defined in the same file.

I don't think your approach would work quite as you expect anyway.  Message fields are not pointers, so you can't have a recursive object at runtime.  That is, if you do:

  message A {
    optional B b = 1;
  }
  message B {
    optional A a = 1;
  }

If you have an instance of A called foo, you *cannot* make it so that foo.b.a == foo -- foo.b.a is an entirely different object.

If you need to be able to down-cast (that is, given a pointer to the superclass, you need a way to get a pointer to the subclass), then you need the superclass to look like:

  message Superclass {
    // One of the following will be filled in.
    optional Subclass1 subclass1 = 1;
    optional Subclass2 subclass2 = 2;
    ...
  }

You can use extensions instead of normal fields, which is often useful for these cases.  Other than that, though, there's no other option.  You cannot convert a Subclass1 back to a Superclass, so you have to pass around pointers to the Superclass itself and simply say "This object must contain a subclass1".

In general, you may find that trying to shoe-horn a concept of inheritance into protocol buffers doesn't work well.  Unfortunately, classic object-oriented inheritance does not translate well to message formats sent over the wire.  Extensions, on the other hand, are very straightforward on the wire, which is why that is what protocol buffers provides.

ly.p...@gmail.com

unread,
May 29, 2009, 5:52:46 AM5/29/09
to Protocol Buffers
Thanks for your reply. Indeed i'm using extend now.. it was one of my
possible design, and i was assessing all possible cases.
I have come to this design. And i have a question, what is
AddExtension for ?

#File AbstractClass

option optimize_for = SPEED;

message AbstractClass
{
optional int32 AbstractClassInformation = 1;

extensions 100 to 199;
}


#File ConcreteClass

import "AbstractClass.proto";

message ConcreteClass
{
required AbstractClass super = 1;

extend AbstractClass
{
optional ConcreteClass derived_ConcreteClass = 100;
}
}


And using carefully the Extension API, i.e. letting the nested object
or extend part creation to the Extension API, i think
i managed to obtain a double link between the abstract and concrete
objects.

//test to see field manipulation of Abstract Class from Derived Class
void Test_HD_LinkDerivedToAbstract()
{

ConcreteClass * cc = new ConcreteClass();
AbstractClass * ac = cc->mutable_super();

//42...
cc->mutable_super()->set_abstractclassinformation(42);

//yes it does not create another instance
AbstractClass * ac2 = cc->mutable_super();

std::cout << "test on link derived to abstract " << endl;
std::cout << "information from cc instance " << cc->super
().abstractclassinformation() << endl;
std::cout << "information from ac instance " << ac-
>abstractclassinformation() << endl;
std::cout << "information from ac2 instance " << ac2-
>abstractclassinformation() << endl;
}

//test to see field manipulation of Derived Class
void Test_HD_LinkAbstractToDerived()
{
AbstractClass * ac = new AbstractClass();

//Add extension doesn't seem to work and i dunno why?
//ac->AddExtension(ConcreteClass::derived_ConcreteClass);
ConcreteClass * cc = ac->MutableExtension
(ConcreteClass::derived_ConcreteClass);
cc->mutable_super()->set_abstractclassinformation(42);

ConcreteClass * cc2 = ac->MutableExtension
(ConcreteClass::derived_ConcreteClass);
cc2->mutable_super()->set_abstractclassinformation(422);


std::cout << "test on link abstract to derived" << endl;
std::cout << "information from ac instance " << ac-
>abstractclassinformation() << endl;
std::cout << "information from cc instance " << cc->super
().abstractclassinformation() << endl;
std::cout << "information from cc2 instance " << cc2->super
().abstractclassinformation() << endl;

//test ClearExtension OK
//ac->ClearExtension(ConcreteClass::derived_ConcreteClass);

//test HasExtension OK
bool hasExtension = ac->HasExtension
(ConcreteClass::derived_ConcreteClass);
(hasExtension) ? std::cout << "well done ;) \n" : std::cout << "T_T
have to change your design\n";
}

This current design kind of satisfying me, i will try to test it in
the Java generated Classes :) .

cheers,

Pascal Ly.
> > > Pascal Ly.- Hide quoted text -
>
> - Show quoted text -

Kenton Varda

unread,
May 29, 2009, 5:25:12 PM5/29/09
to ly.p...@gmail.com, Protocol Buffers
You can't create cyclically-linked protocol buffers.  There is no encoding for such a thing.  Sorry.

AddExtension() is equivalent to the add_*() generated accessors.  It only works with repeated fields.
Reply all
Reply to author
Forward
0 new messages