views:

139

answers:

6

Given this class:

class C
{
    private:
        struct Foo
        {
            int key1, key2, value;
        };
        std::vector<Foo> fooList;
};

The idea here is that fooList can be indexed by either key1 or key2 of the Foo struct. I'm trying to write functors to pass to std::find_if so I can look up items in fooList by each key. But I can't get them to compile because Foo is private within the class (it's not part of C's interface). Is there a way to do this without exposing Foo to the rest of the world?

Here's an example of code that won't compile because Foo is private within my class:

struct MatchKey1 : public std::unary_function<Foo, bool>
{
    int key;
    MatchKey1(int k) : key(k) {}
    bool operator()(const Foo& elem) const
    {
        return key == elem.key1;
    }
};
+1  A: 

You could make the functor a friend of C.

Space_C0wb0y
+2  A: 

Yes. Make the functor another member of C and encapsulate std::find_if behind a method of C.

Following is an example:

#include "stdafx.h"
#include <vector>
#include <cassert>
#include <algorithm>
#include <iostream>

class C
{
    private:
        struct Foo
        {
            int key1, key2, value;
        };

        std::vector<Foo> fooList;

    struct Finder
    {
    private:
      int key1, key2;

    public:
      Finder(int k1, int k2)
      {
        key1 = k1;
        key2 = k2;
      }

      bool operator ()(Foo const& foo) const
      {
        return foo.key1 == key1 || foo.key2 == key2;
      }
    };

public:
  C()
  {
    Foo foo1, foo2;
    foo1.key1 = 5;
    foo1.key2 = 6;
    foo1.value = 1;
    foo2.key1 = 7;
    foo2.key2 = 8;
    foo2.value = 10;

    fooList.insert(fooList.begin(), foo1);
    fooList.insert(fooList.begin(), foo2);
  }

  int Find(int key1, int key2)
  {
    return std::find_if(fooList.begin(), fooList.end(), Finder(key1, key2))->value;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  C c;

  std::cout << c.Find(5, 3) << std::endl;
  std::cout << c.Find(3, 6) << std::endl;
  std::cout << c.Find(7, 3) << std::endl;
  std::cout << c.Find(3, 8) << std::endl;

  return 0;
}
+1  A: 

I'd do something like this.

Header:

class C
{
private:
    struct Foo
    {
        int index;
        Bar bar;
    };

    // Predicates used to find Notification instances.
    struct EqualIndex;
    struct EqualBar;

    std::vector<Foo> fooList;
};

Source:

// Predicate for finding a Foo instance by index.
struct C::EqualIndex : std::unary_function<C::Foo, bool>
{
    EqualIndex(int index) : index(index) { }
    bool operator()(const C::Foo& foo) const { return foo.index == index; }
    const int index;
};

// Predicate for finding a Foo instance by Bar.
struct C::EqualBar : std::unary_function<C::Foo, bool>
{
    EqualBar(const Bar& bar) : bar(bar) { }
    bool operator()(const C::Foo& foo) const { return foo.bar == bar; }
    const Bar& bar;
};

Usage:

// Find the element containing the Bar instance someBar.
std::vector<Foo>::iterator it = std::find_if(fooList.begin(),
                                             fooList.end(),
                                             EqualBar(someBar));

if (it != fooList.end())
{
    // Found it.
}

Sort of...

Johann Gerell
+1, I forgot about simply forward declaring the functors to be nested structs as well. I fixed a typo (copy-and-paste-o?) for you as well.
Kristo
@Kristo: copy-and-paste-o? you bet-o! ;)
Johann Gerell
+1  A: 

The syntax is pretty baroque, but I could turn fooList into a boost::multi_index_container indexed on key1 and key2.

Kristo
+1, for all the times I suggested multi_index and never got upvoted ;)
pmr
A: 

If you don't need your struct inside your header you could also use unnamed namespaces in your implementation file to make the definitions and declarations local to the compilation unit (with static being the C-like alternative static).

This leaves you with a cleaner header that isn't obscured by implementation details.

pmr
I think `Foo` still needs to be declared in the header because `fooList` depends on it.
Kristo
@Kristo: Certainly, but the question was about the functor not the struct itself. The real question is if the struct Foo is really just an implementation detail or if it should be provided outside of your class.
pmr
A: 

I could use the Pimpl Idiom to hide the private section of C inside another class. Since everything in CImpl can safely be public, I should be able to do whatever I want with Foo there.

Kristo