7

I have the following. The struct is prototyped so it compiles fine.

struct vertexNodeInfo
{
    vector<vertexNodeInfo> node;
};

I'm trying to write an octree thingy. What I want to do is use a recursive function to continue adding a node to each node until I get down to a specific point, at which time the function, rather than adding another node, adds a leaf. I want to use no memory when there's no further node or leaf added if that's possible.

Maybe templates would help in this situation, but I'm not sure how to use them...

I don't think I've explained myself well. Here's a diagram:

branching recursive struct

I have no idea if what I'm asking for is impossible or too confusing to understand or just plain dumb, but I can't figure it out on my own. I'm sorry that I can't explain it any better.

I'm using C++98/03 (VC++2008) and cannot use C++11

Any help at all would be much appreciated.

ADDITIONAL INFO:

Better explanation: I want an array of an array of an array of an array of data. Memory usage is very important in this (I'm storing several million elements, so a single byte makes a huge difference). Each array can contain 8 more arrays, but until I need to use it I want each one of the arrays to use no memory. It's an octree of sorts.

MORE ADDITIONAL INFO:

Here's another diagram. It's a little big, so you might need to right click it and select Open image in new tab to make it readable.

What I don't want are "brown" (red+green) boxes, where every box reserves memory for both more nodes and for the leaf data. That would use far too much memory for my needs.

This is basically what I'm trying to achieve, pictured as 2D for simplicity:

2D example of my idea of an octree

Clonkex
  • 3,373
  • 7
  • 38
  • 55
  • 2
    It looks like an implementation of a linked list (from your diagram) – KingJohnno Oct 20 '13 at 12:04
  • no idea what you're trying to do. give a concrete example (input+output pair) – Karoly Horvath Oct 20 '13 at 12:04
  • @KarolyHorvath I added more info to my question. Hopefully should be more understandable now. – Clonkex Oct 20 '13 at 12:15
  • You're trying to implement "an octree thingy"? That level of precision won't get you far in programming, son! – Lightness Races in Orbit Oct 20 '13 at 14:47
  • `I want an array of an array of an array of an array of data` First it was an octree, then it was a linked list, and now it's an array? Make up your mind! – Lightness Races in Orbit Oct 20 '13 at 14:48
  • @LightnessRacesinOrbit The arrays _are_ the implementation of the octree. Each array should contain 8 elements; each element is another array. I have no idea what a linked list is. I'm really struggling to get my head around erenon's and sehe's answers. Talk about complex. The fact that I understand very little of sehe's code (what does `<<` mean??) makes me more inclined to go with erenon's answer, but I'm still not entirely sure how that works either. You're dealing with a fairly inexperienced C++ programmer here, go easy on me ;) – Clonkex Oct 20 '13 at 21:43
  • There's a new diagram in the question now. Should make it clearer as to what I want/need. – Clonkex Oct 20 '13 at 22:05
  • If you don't even know what `<<` is, I suggest learning C++ basics with a much simpler project, first. – Lightness Races in Orbit Oct 20 '13 at 22:52
  • Ok, badly put, I do know what it is: bitwise shift left operator. It moves all the bits in the data to the left. What I mean is I don't know why it's being used there and why it's being overloaded. Also, I find complex use of templates confusing. – Clonkex Oct 20 '13 at 23:21
  • Are the items you are inserting bounding boxes, or points? This is an important detail when considering octree implementation. – Puppy Oct 21 '13 at 00:34
  • Ah, sorry, points. Each red box is just data, and knows nothing of size. – Clonkex Oct 21 '13 at 00:53
  • As far as I can see, there are at least two ways of implementing the green boxes. The first and possibly better method would be to have them know their own size as a single int (eg. 512) and then when you want to add in the next layer of boxes, you make them half the size of their parent (eg. 256). When you reach 1, you create red boxes. The other method would be a layer number. You multiply it by 2 and it gives you the size of the box. At layer 1, the boxes are red. – Clonkex Oct 21 '13 at 01:09

4 Answers4

8

Without any (manual) heap allocation[1]:

struct NodeInfo { 
    int id; 
};

using Tree = boost::make_recursive_variant<
        NodeInfo,
        std::vector<boost::recursive_variant_>
    >::type;

I know variants come with their own "complexity", but memory locality is preserved and manual memory management avoided.

Now to get closer to your stated optimization goals, you could use std::array<T, 8> instead of the std::vector, or perhaps just make the vector use a custom allocator to allocate from a memory pool.

Sample program (see it Live on Coliru):

#include <iostream>
#include <boost/variant.hpp>
#include <vector>

struct NodeInfo { 
    int id; 
};

using Tree = boost::make_recursive_variant<
        NodeInfo,
        std::vector<boost::recursive_variant_>
    >::type;

// for nicer code:
using Branch = std::vector<Tree>;
using Leaf   = NodeInfo; 

static std::ostream& operator<<(std::ostream& os, Leaf const& ni) { 
    return os << ni.id; 
}
static std::ostream& operator<<(std::ostream& os, Branch const& b) { 
    os << "{ ";
    for (auto& child: b) os << child << " ";
    return os << "}";  
}

int main()
{
    Branch branch1 { 
        Leaf { 2 }, 
        Leaf { 1 }, 
        Branch { 
            Leaf { 42 }, 
            Leaf { -42 }, 
        }
    };

    Tree tree = Branch { branch1, Leaf { 0 }, branch1 };

    std::cout << tree << "\n";
}

Prints:

{ { 2 1 { 42 -42 } } 0 { 2 1 { 42 -42 } } }

[1] (outside the use of std::vector)

sehe
  • 374,641
  • 47
  • 450
  • 633
  • One thing: why `auto branch1 = Branch {` instead of `Branch branch1 {`? – Lightness Races in Orbit Oct 20 '13 at 14:50
  • @LightnessRacesinOrbit I liked it when my sample tree was much smaller. Lemme change it – sehe Oct 20 '13 at 14:52
  • There's a new diagram in the question now. It should make it a bit clearer as to what I want. – Clonkex Oct 20 '13 at 22:05
  • I can't understand this answer. It's far too complicated for me. I just want a small, simple implementation, just an entire system that needs a separate file to manage it all. My project is only very small, and this code is bigger and more complicated than any other part of the project. And once again, I don't understand what's going on. Not saying your code is bad, just too complicated for me :) – Clonkex Oct 20 '13 at 22:42
  • @Clonkex Okay, fair enough. I suppose that (e.g. coming from C) you'd want to stay closer to the bare metal - but in my experience that will lead to (a) a lot more code (b) compromises and (c) more room for error. I'd suggest looking at the [`Boost Pointer Container Library`](http://www.boost.org/doc/libs/1_54_0/libs/ptr_container/doc/ptr_container.html) if you want to follow erenon's answer, or use [`Boost Container Library`](http://www.boost.org/doc/libs/1_54_0/doc/html/container/containers_of_incomplete_types.html#container.containers_of_incomplete_types.recursive_containers) because it ... – sehe Oct 20 '13 at 22:49
  • ... directly supports recursive containers (see that link). (Oh **P.S.** Did you notice that my example contained copying and cloning subtrees, as well as traversing all nodes for displaying, without much additional effort? And it's without memory leaks? I mean, that's _why_ I appreciate using a library here. I still understand if you don't want to, of course) – sehe Oct 20 '13 at 22:50
  • Coming from [BASIC](http://www.thegamecreators.com/?m=view_product&id=2000) actually :) I do realise C++ is an enormously powerful language with incredible flexibility of use, but I have big gaps in my knowledge, and when there's lots of special symbols and more advanced stuff all squished into a small space, I get confused. However I really want/need to learn the more advanced stuff so I can be a better programmer. So, let's dissect it somewhat. First: Are `Tree`, `Branch`, and `Leaf` part of Boost? Second, I have never seen `using` used that way. Lastly, why are you overloading `<<`? – Clonkex Oct 20 '13 at 23:45
  • P.S. lol, the only sentence I understand in that is "And it's without memory leaks". So no, I didn't notice. *reads more carefully* Ok, I thought about it some more and now I understand. But I don't understand the syntax you used to do that. – Clonkex Oct 20 '13 at 23:47
  • @Clonkex maybe you like the approach using smartpointers better, it might be worth a look (see my [comments at ereon's answer just now](http://stackoverflow.com/questions/19477055/c-branching-recursive-struct/19478693?noredirect=1#comment28898697_19477111)). It may be clear why I don't like this - but don't let that stop you from using the other approach, there might be valid reasons why it's not so bad, and it's possible to tweak (just like here, e.g. using proper allocators) – sehe Oct 21 '13 at 00:23
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/39607/discussion-between-sehe-and-clonkex) – sehe Oct 21 '13 at 00:25
  • 1
    18 lines is "bigger and more complicated than any other part of the project"? Must be one terse project! – Lightness Races in Orbit Oct 21 '13 at 12:13
  • `Tree`, `Branch` and `Leaf` were all declared _in this code_; `using` is a more powerful version of `typedef` (in this context) that comes in C++11. He overloaded `<<` only to present demo output to you (`operator<<` is the canonical write-to-stream operator). – Lightness Races in Orbit Oct 21 '13 at 12:14
  • @LightnessRacesinOrbit see the chat discussion as well. I think he means "big" when counting the inclusion(s) from/of boost – sehe Oct 21 '13 at 18:13
  • 2
    Ok well easy solution don't count it :p – Lightness Races in Orbit Oct 21 '13 at 19:20
  • @LightnessRacesinOrbit FWIW check out the part of Sutter's recent(ish) post [Almost Always Auto](http://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-almost-always-auto/) headed **(b) auto x = type{ init }** for pros of that syntax. – boycy Oct 22 '13 at 12:02
  • @boycy: Sutter has always been `auto`-happy. `the main reasons to declare variables using auto are for correctness, performance, maintainability, and robustness—and, yes, convenience` -- I maintain that in the majority of cases the opposite is true of most of these points. But I've beaten this horse to death in the Lounge SO chat. As for the specific point you link me to, here we don't want a conversion and there's no risk of failing to initialize. It's just using `auto` for its own sake, and it adds nothing. `Branch branch1 { ... }` can't go wrong. – Lightness Races in Orbit Oct 22 '13 at 12:05
  • @LightnessRacesinOrbit Except it added unifirmity to the declaration lines. I sometimes value esthetics too. However, as i already mentioned, the sample had grown such that the esthetical reasons didn't apply anymore – sehe Oct 22 '13 at 12:29
  • 1
    @sehe: Oh, I suppose it did, on the RHS. I'll give you that. Frankly I think that's even more confusing, though, since the LHSs did completely different things but this was hidden by the `auto`. – Lightness Races in Orbit Oct 22 '13 at 12:53
  • @LightnessRacesinOrbit: sehe's point on the visual uniformity was the potential benefit I thought might've been the reasoning for it. (FWIW I use auto prolifically, with greater clarity and flexibility resulting; I am trialling the `auto var = type{...}` approach and am so far unconvinced...) – boycy Oct 22 '13 at 16:43
  • 1
    @boycy: Nobody has yet been able to convice me that `auto` will ever add clarity except in cases where an obvious and long-winded _type-name_ is effectively being aliased to the sequence of characters "auto" in the code, but you're welcome to try ;) I've at least been able to convince several semi-prominent C++ers that it oughtn't be used in every bloomin' case. – Lightness Races in Orbit Oct 22 '13 at 16:59
  • In this case, @Lightness it's not strange to use auto because we don't really "care" about which type of node we declared: `auto node1 = Leaf { 1 };` or `auto node2 = Branch { node1 }`. We could do `Tree tree; tree = node1;` or `tree = node2;` equally well. But, it's all stylistic only and you're right, I was expressly focusing on variable names over types in this sample. I wouldn't argue it added clarity, but it sure made the code express it's intent better, because I know how I intended it: high-level code. Yes. "Scripting languages", I know :) (In fact, you know I agree with your concerns) – sehe Oct 22 '13 at 21:24
2

The core structure of the octree is

struct Node {
    std::vector<T> items;
    std::array<std::unique_ptr<Node>, 8> subnodes;
    Box BoundingBox;
};
class Octree {
    Node n;
    //... stuff
public:
    Octree(Box location)
       : n(location) {}
};

If you're desperate for a few extra bytes on the leaf nodes (and a few bytes lost on the non-leaf nodes), you can try using a pointer to the subnodes array rather than holding it by value.

Now, if T is a point, then you can get away with using a boost::variant to store only the items or the subnodes, because each point is guaranteed to exist in exactly one subnode, and you can pick an arbitrary cutoff point between having items and having subnodes.

Else if T is a kind of bounding-box, you cannot get away with this, because the bounding boxes that do not fit completely into any of the subnodes must go into the items list, so the items list must exist regardless of whether or not there are subnodes.

What I'm also going to say is that if you're desperate for either time or space optimizations, you should seriously look into custom memory allocation routines.

Edit: Yes, I used an array of pointers, rather than a pointer to an array. The long and short is that describing the correct initialization of that array without some strong C++11 support is a complete bitch and in my personal use, it didn't warrant the serious issues I had actually making the damn thing. You can try std::unique_ptr<std::array<Node>, 8> if you want. It should, in theory, be the superior choice.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • I think the ultimate answer to the _real_ question (i.e. seeing beyond the mundane implementation challenges) is in the last two lines. I lend you my +1 for this – sehe Oct 21 '13 at 00:51
  • Sorry, I thought I had put that I was using C++98 only. Turns out I hadn't. I can't use anything from C++11, so I assume I can't use `std::unique_ptr`. – Clonkex Oct 21 '13 at 01:19
  • Or C++03, since it's effectively the same as 98 and I'm using VC++2008. – Clonkex Oct 21 '13 at 01:41
  • @Clonkex: That's not entirely the same. VC++2008 has a couple of very small C++11 features like `>>` closing for templates, and more importantly, it has TR1. However, `auto_ptr` will suffice as a replacement for `unique_ptr` in this case (just be careful what you do with that thing). If, however, you're serious about performance, you *will* want to upgrade your compiler. Move semantics are just too big a boost to turn down. – Puppy Oct 21 '13 at 06:29
  • Yes, I did some more research and was hoping `auto_ptr` would suffice, but I wasn't sure if it did the same thing. I'm not too serious about performance as such, because my current very slow method is just about fast enough without full optimisation, so this method is going to be more than fast enough. If it's not, I'll research whether going to VC++2012/13 would be compatible with my libraries. I'm more worried about memory usage. Thanks :) – Clonkex Oct 21 '13 at 12:11
0

What about polimorphism?

struct TreeElem {
    virtual ~TreeElem() {}
};

struct Node : public TreeElem {
    std::vector<TreeElem*> _children;
};

struct Leaf : public TreeElem {
    int _value;
};

You can figure out the rest (virtual members of TreeElem).

P.S: if it's more than something trivial, use smart pointers.

erenon
  • 18,838
  • 2
  • 61
  • 93
-1

Check che composite pattern and you can adapt it easily to perform an octree. After this, create the recursive function that take as argument the actual octree depth, so you can easyli perform what you want. Unfortunately, I don't understand well your question so I can't be more precise.

Jepessen
  • 11,744
  • 14
  • 82
  • 149