how friend function has been implemented internally
Asked Answered
C

1

5

We all have used friend function both global level as well as class level in C++. I tried to search accross the internet how internally friend function has been implemented.

What manipulation done by the "friend" keyword. For exampale we know how v-ptr and v-table is implemented internally, I am looking for the same kind of answer.

Please Note: This question is not related to, how to use friend function or usage of friend function.

Caelum answered 14/4, 2015 at 10:43 Comment(11)
There is nothing special about the run-time implementation of a friend function. It is merely a compile-time mechanism for allowing a function to access private members of a class.Joses
Thanks for the comment . @JosephMansfield I undrestand what you are indicating but that compile-time mechanism I am trying to understand. Can you put some link so I that can easyly refer.. As I have listed V-table and V-ptr also compile time modification but we know how that comile time modification is happening.Caelum
Additional questions: Isn't that compiler dependent? What does the C++ standard say?Thermostat
The compile-time mechanism is (in a much simplified form): "Does this function have access to this member? Well, is it a member function of that class? No. Is it a member of a derived class? No. [etc.] Is it a friend function? Yes! It has access."Joses
@Thermostat Well the C++ standard says exactly the rules of the friend keyword. The compiler can implement it however it likes, of course. Why would that matter? Normally we're concerned with the difference in compiled output between different compilers.Joses
@Caelum No, virtual functions are a run-time mechanism. It changes the compiled output. As you say, the executable will create v-tables and such. A friend function is not like this. It simply influences whether the program will actually compile or not. If you write program that has a function and compiles whether that function is friend or not, adding the friend keyword has no effect on the compiled output.Joses
@JosephMansfield yes I agree with you and I know this. But i want to know if anything else is performed by the compiler.Caelum
@Caelum It's hard to know what to answer this with. The compiler just does what the standard says it should do with regards to the friend keyword. How it does that is up to the compiler.Joses
Any implementation details of friend are going to be of a completely different kind of detail than vtables. This reads like "I want to know how sand is used, like how we use axioms to prove theorems" -- it looks like it makes sense, but only if you have no idea what sand, axioms and theorems are. Any analogous answer to your 'like' is a huge stretch. I have no idea what you are asking.Antung
In other words, there is simply no implementation detail like vtables for friend functions.Joses
@Caelum Protection levels are purely a compile-time concept. There is nothing for the compiler to perform beyond allowing the "friend" to access protected members during compilation. This is probably implemented by looking things up in a table somewhere.Dutchman
S
7

For the purposes of this answer I searched on a very old version on gcc, because it is smaller, and easier to reason about. Implementation in newest versions or in other compilers may be completely different.

The source files searched for can be viewed on opensource.apple.com

gcc has a function is_friend(), defined in friend.c, which basically returns if a function or a type is a friend. Here is the relevant code for functions:

int
is_friend (type, supplicant)
     tree type, supplicant;
{
  int declp;
  register tree list;
  tree context;

  if (supplicant == NULL_TREE || type == NULL_TREE)
    return 0;
/*
Comment added by me: The following defines are in tree.h
#define TREE_CODE(NODE) ((enum tree_code) (NODE)->common.code)
#define TREE_CODE_CLASS(CODE)    tree_code_type[(int) (CODE)]
This is expanded as:
declp = (tree_code_type[(int) (((enum tree_code) (supplicant)->common.code))] == 'd')
*/
declp = (TREE_CODE_CLASS (TREE_CODE (supplicant)) == 'd');
// That is, it will simply search the array for the code of the supplicant and check if it is a function declaration.

  if (declp)
    /* It's a function decl.  */
    {
      tree list = DECL_FRIENDLIST (TYPE_MAIN_DECL (type));
      tree name = DECL_NAME (supplicant);
      tree ctype;

      if (DECL_FUNCTION_MEMBER_P (supplicant))
    ctype = DECL_CLASS_CONTEXT (supplicant);
      else
    ctype = NULL_TREE;

      for (; list ; list = TREE_CHAIN (list))
    {
      if (name == FRIEND_NAME (list))
        {
          tree friends = FRIEND_DECLS (list);
          for (; friends ; friends = TREE_CHAIN (friends))
        {
          if (same_type_p (ctype, TREE_PURPOSE (friends)))
            return 1;

          if (TREE_VALUE (friends) == NULL_TREE)
            continue;

          if (supplicant == TREE_VALUE (friends))
            return 1;

          /* With -fguiding-decls we are more lenient about
             friendship.  This is bogus in general since two
             specializations of a template with non-type
             template parameters may have the same type, but
             be different.  

             Temporarily, we are also more lenient to deal
             with nested friend functions, for which there can
             be more than one FUNCTION_DECL, despite being the
             same function.  When that's fixed, the
             FUNCTION_MEMBER_P bit can go.  */
          if ((flag_guiding_decls 
               || DECL_FUNCTION_MEMBER_P (supplicant))
              && same_type_p (TREE_TYPE (supplicant),
                      TREE_TYPE (TREE_VALUE (friends))))
            return 1;

          if (TREE_CODE (TREE_VALUE (friends)) == TEMPLATE_DECL
              && is_specialization_of (supplicant, 
                           TREE_VALUE (friends)))
            return 1;
        }
          break;
        }
    }
    }
  else
    /* It's a type.  */
    {
    // ...
    }

  if (declp && DECL_FUNCTION_MEMBER_P (supplicant))
    context = DECL_CLASS_CONTEXT (supplicant);
  else if (! declp)
    /* Local classes have the same access as the enclosing function.  */
    context = hack_decl_function_context (TYPE_MAIN_DECL (supplicant));
  else
    context = NULL_TREE;

  /* A namespace is not friend to anybody. */
  if (context && TREE_CODE (context) == NAMESPACE_DECL)
    context = NULL_TREE;

  if (context)
    return is_friend (type, context);

  return 0;
}

Basically, it gets the list of friends of a particular type, and iterates through it, checking if any of them is equal to the function being tested. Friend functions are added to a type by using similar functions defined in the same source file: add_friend(), add_friends() for all member functions of a class, make_friend_class() for classes, etc.

I'm guessing it uses this function to determine, when checking for access (The moment where it may yell at you for accessing a private member), if the function has access or not.

The answer to your question is(at least for this old version of GCC): The compiler has a list of friends for each class, and iterates this list to determine if a function is a friend(during compile-time, of course.). If it is, it simply grants access. There's no special code generated for this: It is a regular function call.

Stood answered 14/4, 2015 at 13:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.