Marti Markov
Marti Markov

Reputation: 766

How should I write a property declaration for C array in ObjC?

I currently have this code:

@interface Matrix4 : NSObject
{
    float mat[16];
}
@property (readonly) float mat[1];

I want the property to either give me the mat array or have multiple properties giving me readonly access to mat[1], mat[2], etc.

I current have "Property cannot have array of function type float[1]" as an error message

Upvotes: 2

Views: 1702

Answers (2)

Gabriele Petronella
Gabriele Petronella

Reputation: 108101

As the compiler is telling you, properties cannot have array or function type.

You can manually implement the getter, like

@interface Matrix4 : NSObject {
    float mat[16];
}
- (float *)mat;

@implementation
- (float *)mat {
    return mat;
}

or you can consider using an NSArray instead, depending on your requirements. NSArray is definitely more overweight than a native C array, but it allows you to use properties.


However I suspect you have a design issue: it looks like you are trying to implement a squared matrix, but you are exposing the internal representation, most likely so that the client can set the matrix elements.

You should instead hide the internal representation and only expose methods to perform matrix operations. For instance, you can think of exposing a method which sets the matrix value, as:

- (void)setValue:(float)value forRow:(int)row column:(int)col {
    NSParameterAssert(row >= 0 && row < 4 && col >= 0 && col < 4)
    mat[row * 4 + col] = value;
}

and one that gives you an element back

- (float)valueForRow:(int)row column:(int)col {
    NSParameterAssert(row >= 0 && row < 4 && col >= 0 && col < 4)
    return mat[row * 4 + col];
}

and make the mat ivar private. This gives you also the flexibility of changing the internal representation at will, without breaking the client's code.

The above implementation is also very easy to generalize to a squared matrix of size, by providing a dimension parameter and using a NSArray or dynamic memory allocation (since variable-length arrays cannot be ivars).

Upvotes: 1

Arkku
Arkku

Reputation: 42129

Arrays cannot be return values, so the property cannot have an array type. Instead you must return a pointer, so declare the property as a pointer to the element type of the array:

@property (readonly) float *mat;

Keep the instance variable as float mat[16] as you have now. Then implement the accessor to return a pointer to the array:

- (float *)mat {
    return mat; // array decays to pointer automatically
}

Alternatively, you could have an accessor directly for the individual elements:

- (float)matIndex:(NSUInteger)i {
    // maybe check bounds here?
    return mat[i];
}

The problem with these approaches is that the information about the size of the array is lost, so you would probably want to put the size of the array in a macro or const variable. If you need something a bit more object-oriented, make the array an NSArray and store NSNumbers in it.

edit: One option would also be to wrap the array in a struct to preserve the size info, though you still probably want to pass it around by reference:

struct matrixf16 {
    float f[16];
};
@interface Matrix4 : NSObject {
    struct matrixf16 mat;
}
@property (readonly) struct matrixf16 *mat;

(Also, if I'm guessing correctly that the size is 16 because it's meant to hold a 4×4 matrix, why not make the array float f[4][4].)

Upvotes: 5

Related Questions