2

Sorry this is a popular subject (eg here), but I could not find a suitable solution to my problem. Showing code would be too long and confusing, so I am just showing peudo-code; this is a question about design and I am looking for a pattern applicable in C++(11) here.

Goal: I am dealing with multi-dimensional arrays (say, a 4D array containing a vector field), and I want to access array elements with n-D coordinates and dynamic boundary conditions.

Objects:

  • Array: contains the data as a 1D array in memory and overloads subscripting (value_type operator[] (unsigned));
  • Indexer: stores the dimensions of the n-D array, and converts n-D coordinates to 1D indices (unsigned operator() (Coordinates));
  • Boundary: either transforms n-D coordinates (like a periodic boundary) or returns a default value (eg, null values outside the container).

Requirements

  • The coordinates type should be templated (any numeric type should be accepted);
  • Boundary conditions can be dynamically modified.

Problem:

Given a n-D container with boundary conditions, I would like the request of a value at given coordinates to look like this:

value_type operator() ( Coordinates )
{

    if ( Boundary.transforms_coordinates )
    {
        /**
         * 1) Ensure the coordinates are inbound by applying boundary conditions; 
         * 2) Use the Indexer to find the corresponding 1D index.
         */
        return Array[ Indexer(
            Boundary.transform( Coordinates )
        ) ];
    }
    else
    {
        if ( Indexer.coordinates_are_inbound( Coordinates ) )
            return Array[ Indexer(Coordinates) ];
        else
            return Boundary.outbound_value;
    }

}

The problem I can't solve is this: if the boundary is dynamically set, its type cannot be a template value, so it needs to be a pointer to a boundary interface. However, this interface cannot define a virtual template method transform to accept coordinates of different types (let alone a pure virtual one).

I'm guessing there is a fundamental design flaw here, and I would be most grateful if you could show me where, and ideally how to correct it. Sorry as well for the length of the question; this may be solved by design (proto-)pattern I don't know about.

Community
  • 1
  • 1
Jonathan H
  • 7,591
  • 5
  • 47
  • 80
  • "The coordinates type should be templated (any numeric type should be accepted);" -- why? How would int64 and double not be sufficient? And do you need double? – Yakk - Adam Nevraumont Aug 04 '14 at 14:10
  • So I guess I could systematically convert floating point values to double and integer values to int64 you are right, but then it doesn't technically solve the fact that different coordinates containers could be accepted (pointer+size, or fixed size array, or vector, etc). Constraining the container to a specific object could work too, but this lacks flexibility and adds to the complexity of the code. – Jonathan H Aug 04 '14 at 14:29
  • This is an acceptable solution though; I could create IntegerCoordinates and DoubleCoordinates objects, and implement adapters from various containers within these objects. – Jonathan H Aug 04 '14 at 14:31
  • 2
    @sh3lijohn If those are your 3 containers, write `array_view` which can cost-free view any of those. And limit coordinate scalar types to something reasonable (`int` or `size_t`) – Yakk - Adam Nevraumont Aug 04 '14 at 15:11
  • @Yakk Thanks a lot, I think your comments show that I need to refine a little bit more what I call "coordinates", and this is probably why I got stuck in the first place. :) – Jonathan H Aug 04 '14 at 15:19
  • oh, and odds are any Indexer actually wants some particular format: have that format determine the type erased argument. As the user needs to know that anyhow. – Yakk - Adam Nevraumont Aug 04 '14 at 18:47

1 Answers1

0

I want to thank @Yakk for his brilliant suggestions, but I think I am going to go with an implicit extension of my array container in order to access boundary values.

This is motivated by the fact that I don't need a full blown container interface when I want to access values that are potentially subject to boundary conditions, but rather simply an access method; I should know when I need boundary conditions in my program and when I don't. Here is a pseudo code:

struct BoundedContainerAccessor
{
    Array *data;
    Indexer *indexer;

    void accept( Array& a, Indexer& i )
    {
        data = &a;
        indexer = &i;
    }

    template <class T>
    value_type operator() ( T coordinates )
    {
        if (indexer->coordinates_are_inbound( coordinates ))
        {
            return data->[indexer->( coordinates )];
        }
        else
        {
            // Implementation-specific, either:
            // return a value, throw an error, or transform coordinates
        }
    }
};
Jonathan H
  • 7,591
  • 5
  • 47
  • 80