Skip to content
SciTools Blog
Menu
  • Home
  • Blog
  • Support
  • Contact
  • Login
  • Pricing
  • Free Trial
Menu
man using black laptop computer

Friends Don’t Let Friends Have C++ Friends

Posted on January 4, 2023

Abstract: An interactive report to show all the references that depend on C++ friend statements.

C++ classes can have friends. These friends are allowed to access private and protected class members. Because this access breaks the data encapsulation paradigm, friends are generally discouraged. But, suppose the friendship already exists? What would it take to sever ties?

You can download the functional Interactive Report here.

Find My Friends

Let’s use Understand to answer this question. We want to find all of the references that depend on the friend relationship. Let’s assume we’re using the Python API and that we already have the class entity with a friend relationship in a variable called “ent.” We’ll talk about how to get the class entity last.

The first thing we want to find is the list of friends. We access friends by looking for “friend” references, like this:

ent.refs("c friend")

It’s possible to have multiple friends, and for each friend we’ll want to store more information. So, let’s store our friends as dictionary keys. We’ll use the entity rather than the reference as the key because we’ll be comparing entities later. We’ll store the reference in a dictionary for later too.

  friends = {}
  frefs = {}
  for ref in ent.refs("c friend"):
    friends[ref.ent()] = {}
    frefs[ref.ent()] = ref

Finding Dependent References

Our friends have access to the private and protected members. So, let’s start with that list of members. In this case, we only care about each member once, so we use unique=True to have, at most, one reference to each member.

  members = {}
  for ref in ent.refs("define,declare","protected member, private member", True):
    member = ref.ent()

Now we need to look at each reference for this member to see if it depends on a friendship. Suppose class B is my friend, and the B::foo() function calls the protected function bar. The reference I’m looking at would be something like this:

  • ent: B::foo()
  • scope: A::bar()
  • kind: callby
  • file, line, and column

We need some way to know that B::foo() is part of our friend class B and not part of our current class A. We can find the class by following the parent chain of B::foo(). If the referenced entity or any of its parents is one of our friends, then the reference depends on that friend specifier.

We can also make one other assumption: only reverse references are potential friend references. In the sample, bar being called by foo is a potential friend reference because bar is the entity being called. In the reverse case, if A::bar calls B::foo, then that reference is going out of class A and therefore doesn’t depend on A’s friends.

Finally, we’ll store these references in two different two-layer dictionaries. The first dictionary will go from friend to member to list of references. The second goes from member to friend to list of references.

    # Look for references that belong to a friend's scope
    friendrefs = []
    for ref in member.refs():
      # only worry about reverse refs (setby, useby, callby, etc)
      if ref.isforward():
        continue

      par = ref.ent()
      while par:
        if par in friends:
          friends[par].setdefault(member, []).append(ref)
          members[member].setdefault(par,[]).append(ref)
        par = par.parent()

Interactive Reports

With the code snippets above, we can generate the information we want, but it’s not very accessible right now. We haven’t decided how to get the class entity to start with or how to display the information.

Let’s use an interactive report. Interactive reports are plugins for Understand. They can be run on entities, architectures, or the entire database. The report output is displayed in Understand and can sync to other Understand views. 

To make our script into an interactive report, we have to define two required functions. The first one is the name that will be displayed in the UI. I’ll name the report “Friend References.”

def name():
  """
  Required, the name of the ireport.
  """
  return "Friend References"

The second function is the generate function. The generate function has two parameters: a report object and the target. The report object is used to send output to Understand to display. The target can be an entity, architecture, or the entire database. The top of the generate function will be the code we’ve written so far to find and group the references.  

def generate(report, ent):
  """
  Required, generate the report
  """

We need to define one more thing for a working ireport. The test_entity, test_architecture, and test_global functions are used to determine if an entity, architecture, and database target are valid. If these functions are not defined, they are assumed false. So, right now, our interactive report will never be valid and thus never be visible. We’ll define the test_entity function to enable this report for any C++ class that has at least one friend reference.

def test_entity(ent):
  """
  Optional method, return true if report is valid for the entity
  """
  # This is valid for c class's that have a friend reference
  return ent.kind().check("c class, c struct") and len(ent.refs("c friend")) > 0

At this point, our script will be visible. But it doesn’t output anything. We also need the code in the generate function that prints output to Understand. You can refer to the full plugin to see that code.

The Results

To access this report from Understand, it needs to be able to find the plugin. Understand looks for plugins by searching for files with a .upl (Perl plugin) or .upy (Python plugin) extension in the application data directory. See this article for platform-specific list of locations. Or, drag the plugin file onto the Understand GUI and click install on the prompt to have Understand copy the file to the correct location for you.

Now, the report will be visible in the right-click menu of applicable entities. I’ll use the git::Blame class in the GitAhead sample project:

Accessing the custom interactive report from the context menu for an entity

The output is shown in the a dock window:

Sample output of the Friend References Interactive Report

Blame declares Repository as a friend on line 56. Clicking that will open that location in the editor. The only reference that depends on that friend statement is a call from Repository::blame() to the protected Blame::Blame constructor on line 844. Entities are shown in blue and can be interacted with just like entities shown in other Understand windows.

As a more complex example, we can try the report on the git::Repository class.

Sample output of the Friend References Interactive Report for the git::Repository class in the GitAhead sample project

Interestingly, the Branch class doesn’t have any dependent references. So, Branch doesn’t have to be Repository’s friend, and that line of code could be removed. From the Group By Member tree, we can also tell that all the friend references go to only two of the potential protected and private members. If those two members were public, none of the friend references would be necessary.

Interested in more interactive reports? This github repository has several you can try, and this article describes some maintainability reports. Or, write your own!

  • Instagram
  • Facebook
  • LinkedIn
  • Twitter
  • YouTube

Learn more about Understand's Features

  • Dependency Graph
    View Dependency Graphs
  • Comply with Standards
  • View Useful Metrics
  • Team Annotations
  • Browse Depenedencies
  • Edit and Refactor

Related

  • API
  • Architectures
  • Business
  • Code Comparison
  • Code Comprehension
  • Code Navigation
  • Code Visualization
  • Coding Standards
  • Dependencies
  • Developer Notes
  • DevOps
  • Getting Started
  • Legacy Code
  • Licensing
  • Metrics
  • Platform
  • Plugins
  • Power User Tips
  • Programming Practices
  • Uncategorized
  • Useful Scripts
  • User Stories
  • May 2025
  • January 2025
  • December 2024
  • November 2024
  • August 2024
  • June 2024
  • May 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • June 2023
  • April 2023
  • January 2023
  • December 2022
  • November 2022
  • September 2022
  • August 2022
  • May 2022
  • April 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • June 2021

©2025 SciTools Blog | Design: Newspaperly WordPress Theme