Skip to main content
Source Link
magnus
  • 111
  • 2

How do compilers implement symbolic (rather than textual) insertion?

From a programming language designer's point of view, how is "symbolic" insertion implemented as opposed to "textual" insertion using pre-processor macros?

I understand the basics of how "textual" insertion works in C and C++. That is, the pre-processor might transform:

#include "person.hpp"

class employee 
{
    person p;
};

into:

#ifndef PERSON_HPP
#define PERSON_HPP

class person
{
public:
    uint32_t height_;
};

#endif // PERSON_HPP

class employee
{
    person p;
};

This is required because the size of the employee class can only be determined if the compiler knows the size of the person class. One disadvantage of this is slow compilation speed. A program consisting of a single source code file main.cpp might have thousands of lines inserted into it by the pre-processor before compilation begins. This can be avoided in some cases by forward declaring the class and using pointers. The employee class cannot use the person class until it is defined, (which is why the implementation file will still include "person.h") but it does remove header file dependencies and increases compilation speed.

class person;

class employee
{
    person* p;
};

For a language like Java, on the other hand, no textual insertion is required. Instead, "symbolic" insertion is performed. So the file below would not be "fleshed out" with the definitions of the dependent software modules.

import com.example.library.person;

class employee {
    private person p;
};

I can't wrap my head around how the compiler accomplishes this. I imagine that because Java is garbage collected, it doesn't need to know the size before it is declared, in much the same way as classes can be forward declared in C++ without including the entire file. But how does the compiler know that p->some_function_that_does_not_exist(23) is valid?

This question was originally asked on stack overflow.