5

I want to declare a friend class only if some (compile-time) condition is true. For example:

// pseudo-C++
class Foo {
    if(some_compile_time_condition) {
        friend class Bar;
    }
};

I did not find any solution on the internet. I went through all the answers to the question Generating Structures dynamically at compile time. Many of them use the C++11 std::conditional, but I would like to know if it is possible to do this in C++03 without using the preprocessor.

This solution https://stackoverflow.com/a/11376710/252576 will not work because friendship is not inherited ( friend class with inheritance ).

Edit Just to make this more easily visible, as mentioned below in the comment: This requirement is unusual. This is part of a new research project in hardware simulation, that I am working on. The testbench is written in C++, and I want to display the variables in a waveform. I have researched various other options, and figured out that I need to use a friend class, due to practical considerations. The friend will capture the values and generate the waveform, but I would prefer to have the friend only when the waveform is required, and not all the time.

Community
  • 1
  • 1
Masked Man
  • 1
  • 7
  • 40
  • 80
  • 1
    I think you need a preprocessor for this – John Dvorak Dec 21 '12 at 06:30
  • 1
    Can you be more concrete with what you're trying to do? You can try the so-called "[passkey idiom](http://stackoverflow.com/questions/3324898/can-we-increase-the-re-usability-of-this-key-oriented-access-protection-pattern)" we/I designed on SO (i.e., not widely known). – GManNickG Dec 21 '12 at 06:31
  • This is a very unusual requirement in practical development (on the other side, this is absolutely OK if doing for theoretical purpose). – Yury Dec 21 '12 at 06:37
  • @GManNickG I am using this in a new research project in hardware simulation. The testbench code is written in C++, and I want to provide a new feature to see the member variable values in a waveform during the simulation. The `friend` class will capture the values and generate the waveform. I want that friend to be present only when the waveform is required, and not always. – Masked Man Dec 21 '12 at 06:38
  • @Yury You are right, it is unusual. I have commented above on the use case. – Masked Man Dec 21 '12 at 06:41
  • If you can't conditionally declare a friend, you can duplicate the function to have one friend version and one without friend. Then you can use the usual template magic to select the right class at compile time. The problem is still how to avoid copy/paste and get nice looking code without using a macro, assuming that your class is longer than just three lines. – mars Dec 21 '12 at 07:50
  • Just always use the `friend` declaration. If it isn't needed it doesn't do any harm. – Pete Becker Dec 23 '12 at 01:41

5 Answers5

7

Use friend std::conditional<C, friendclass, void>::type; where C is your condition. A nonclass type friend will be ignored.

The conditional template is easily implemented in C++03. However since C++03 does not support typedef friends you need to use the following syntax there

namespace detail { class friendclass {}; }

class Foo {
  friend class std::conditional<C, 
    friendclass, detail::friendclass>::type::friendclass;
};

Note that the detail dummy class name needs to match the name of the potential friend in this workaround.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I suppose the note about the dummy class name is related to the `::friendclass` part after the type => could you explain what this is (inject class name I guess) and why it's necessary ? Naively I would have stopped at `friend class std::conditional<...>::type`. – Matthieu M. Dec 21 '12 at 09:07
  • @mat the part after ::type is needed because ::type is a typedef. However unless a typedef was declared in the same scope as the corresponding aliased class, you are not allowed to prefix a typedef name with struct, class or union (or enum). Therefor we access the injected class name, which is not a typedef name. – Johannes Schaub - litb Dec 21 '12 at 09:11
  • @JohannesSchaub-litb: So if an object of type `Foo` is included in different TUs, with different values of `C` is it an ODR violation? I'm thinking yes (based on 3.2/5 - names shall refer to the same entity when looked up). Although in practice it shouldn't ever cause a problem. – Richard Corden Dec 21 '12 at 10:17
  • @richard yes i agree. the condition must have the same value in all the TUs. – Johannes Schaub - litb Dec 21 '12 at 10:21
2

[class.friend]/3 tells this :

A friend declaration that does not declare a function shall have one of the following forms:
friend elaborated-type-specifier ;
friend simple-type-specifier ;
friend typename-specifier ;

therefore it is not possible to conditionally declare friends of a class, without a macro.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
2

It seems, unfortunately, not possible within the C++ compiler: ie, it seems that only the preprocessor may help here. Note: Johannes has a proposal, so there is hope!

However I would note that:

  • friendship does not require you to actually use it
  • friendship is a pure compile-time construct (like access specifiers) and does not incur any runtime penalty on any major compiler

there is no reason not to have unconditional friendship, but only use it if some conditions (static or dynamic) are met.

Note: in the future, this is something that the static_if proposal could cover.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
0

Note: Johannes has pretty much nailed it. In '03 you cannot befriend a typedef - but if you know you have a class, then you can refer to it's injected class name.

Johannes' answer also has the benefit of using standard library functionality which too is always a good thing.

#define some_compile_time_condition 0

class Foo;

template <int Condition> class  TestCondition {
private:
  friend class Foo;
  struct Type {
    struct Bar;
  };
};

template <> class TestCondition<1> {
public:
  typedef Bar Type;
};

struct Bar
{
public:
  void foo (Foo &);
};

class Foo {
private:
  friend struct TestCondition< some_compile_time_condition >::Type::Bar;
  int m_i;
};

void Bar::foo (Foo & foo)
{
  foo.m_i = 0;
}

It's still different to the requirement in that Foo always has a friend, but the befriended class changes based on the value of the option.

An interesting side question is whether it is an ODR violation to have versions of Foo both with and without some_compile_time_condition set to 1.

Community
  • 1
  • 1
Richard Corden
  • 21,389
  • 8
  • 58
  • 85
0

I think you take 1 preprocessor and write your source code inside that.

bool flag = false;
#ifdef _MY_FRIEND_
    friend class sample
    flag = true;
#endif

if (flag)
{
 ...
 ...
 ...
}

class Foo {
#ifdef _MY_FRIEND_
    friend class Bar;
#endif
}

};

Here _MY_FRIEND_ is a preprocessor and if you add that preprocessor then at compile time your class Bar will be the friend class...you can use that preprocssor any where when you want to need class Bar as a friend class.other wise compile without the preprocessor then it wont allow u to add Bar as a friend class of Foo

Please correct me if i understood the question wrong.

Astro - Amit
  • 767
  • 3
  • 15
  • 36