views:

885

answers:

6
A: 

However, the compiler just complains about an "invalid use of non-static data member 'foo::b'"

This is because foo::a and foo::b have different types. More specifically, foo::b is an array of size 2 of ints. Your pointer declaration has to be compatible i.e:

int (foo::*aptr)[2]=&foo::b;

Is it possible to do this at all (or at least without unions)?

Yes, see below:

struct foo
{
  int a;
  int b[2];
};

int main()
{

  foo bar;

  int (foo::*aptr)[2]=&foo::b;
  /* this is a plain int pointer */
  int *bptr=&((bar.*aptr)[1]);

  bar.a=1; 
  bar.b[0] = 2;
  bar.b[1] = 11;

  std::cout << (bar.*aptr)[1] << std::endl;
  std::cout << *bptr << std::endl;
}

Updated post with OP's requirements.

dirkgently
Ideally, I'd want a data pointer to a specific element, which should theoretically work, as the declaration of foo::b[1] is int.
tstenner
Valid point, but you're not answering the OPs question
erikkallen
@erikkallen: Great criticism. So, what value did you add to the entire discussion?
dirkgently
i think he *is* answering the question. whether at the user side one does (bar.*aptr)[1] or (bar.*aptr)[x] .. why does it matter. one can pass x along.
Johannes Schaub - litb
The difference the OP cares about is not (bar.*aptr)[1] vs (bar.*aptr)[x]; it's the difference between those and being able to do bar.*aptr, without any array accessors)
Eclipse
A: 
  typedef int (foo::*b_member_ptr)[2];
  b_member_ptr c= &foo::b;

all works.

small trick for member and function pointers usage.
try to write

char c = &foo::b; // or any other function or member pointer

and in compiller error you will see expected type, for your case int (foo::*)[2].

EDIT
I'm not sure that what you want is legal without this pointer. For add 1 offset to your pointer you should get pointer on array from your pointer on member array. But you can dereference member pointer without this.

bb
He doesn't want a pointer to the array but to a specific element.
erikkallen
+1  A: 

The problem is, is that accessing an item in an array is another level of indirection from accessing a plain int. If that array was a pointer instead you wouldn't expect to be able to access the int through a member pointer.

struct foo
{
  int a;
  int *b;
};

int main()
{

  foo bar;
  int foo::* aptr=&(*foo::b); // You can't do this either!
  bar.a=1;
  std::cout << bar.*aptr << std::endl;
}

What you can do is define member functions that return the int you want:

struct foo
{
  int a;
  int *b;
  int c[2];

  int &GetA() { return a; } // changed to return references so you can modify the values
  int &Getb() { return *b; }
  template <int index>
  int &GetC() { return c[index]; }
};
typedef long &(Test::*IntAccessor)();

void SetValue(foo &f, IntAccessor ptr, int newValue)
{  
    cout << "Value before: " << f.*ptr();
    f.*ptr() = newValue;
    cout << "Value after: " << f.*ptr();
}

int main()
{
  IntAccessor aptr=&foo::GetA;
  IntAccessor bptr=&foo::GetB;
  IntAccessor cptr=&foo::GetC<1>;

  int local;
  foo bar;
  bar.a=1;
  bar.b = &local;
  bar.c[1] = 2;

  SetValue(bar, aptr, 2);
  SetValue(bar, bptr, 3);
  SetValue(bar, cptr, 4);
  SetValue(bar, &foo::GetC<0>, 5);
}

Then you at least have a consistant interface to allow you to change different values on foo.

Eclipse
Right, I just thought it should be possible since the actual array elements might/should have a fixed offset from the beginning of the object.Thanks for the clarification.
tstenner
@Josh: Why are you suggesting it is not possible?
dirkgently
Because he's wanting an int foo::* that he can use to refer to the second element in an array in foo. You can have an int * that points to the second int in a specific foo instance, but you can't make plain int foo::* that refers to the int he wants.
Eclipse
The accessor function can be implemented using a template (`template<int index> int}`), so it's quite easy to generate a function that returns a specefic element.
tstenner
A: 

What you want is not possible.

Let's take this example:

#include <iostream>

using namespace std;

typedef struct tagTest
{
    int a;
    char b;
    double c;
    bool d;
}Test;

int main()
{

    Test a[10];

    for(int i=0;i<10;i++)
    {
        a[i].a = i;
        a[i].b = '0'+i;
        a[i].c = 1.0+i;
        a[i].d = ( (i%2) == 0 ? true : false );
    }

    cout << "Structure size:" << sizeof(Test) << endl;

    return 0;
}

Let's say you find a method to access the "a" member of the "Test" structure. It means you will have something like a pointer to integer.

You will want to access the "a" member this way:

int *a_pointer;
a_pointer[0] to point to a[0].a
a_pointer[1] to point to a[1].a
....
a_pointer[9] to point to a[9].a

which is not possible since the memory is not linear (each integer is at sizeof(Test) away from the previous one).

You could create a class and define something similar to iterators, or use some macros to access each member in a index fashion. This is the best method that I can see.

Iulian Şerbănoiu
The array is a member of the struct, so the integers are next to each other (in most/all implementations).
tstenner
A: 

You can't do that out of the language itself. But you can with boost. Bind a functor to some element of that array and assign it to a boost::function:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/function.hpp>
#include <iostream>

struct test {
    int array[3];
};

int main() {
    namespace lmb = boost::lambda;

    // create functor that returns test::array[1]
    boost::function<int&(test&)> f;
    f = lmb::bind(&test::array, lmb::_1)[1];

    test t = {{ 11, 22, 33 }};
    std::cout << f(t) << std::endl; // 22

    f(t) = 44;
    std::cout << t.array[1] << std::endl; // 44
}
Johannes Schaub - litb
A: 

I'm not sure if this will work for you or not, but I wanted to do a similar thing and got around it by approaching the problem from another direction. In my class I had several objects that I wanted to be accessible via a named identifier or iterated over in a loop. Instead of creating member pointers to the objects somewhere in the array, I simply declared all of the objects individually and created a static array of member pointers to the objects.

Like so:

struct obj
{
    int somestuff;
    double someotherstuff;
};

class foo
{
  public:
    obj apples;
    obj bananas;
    obj oranges;

    static obj foo::* fruit[3];

    void bar();
};

obj foo::* foo::fruit[3] = { &foo::apples, &foo::bananas, &foo::oranges };


void foo::bar()
{
    apples.somestuff = 0;
    (this->*(fruit[0])).somestuff = 5;
    if( apples.somestuff != 5 )
    {
     // fail!
    }
    else
    {
     // success!
    }
}



int main()
{
    foo blee;
    blee.bar();
    return 0;
}

It seems to work for me. I hope that helps.

Jonathan Swinney