<p>In C and C++ (and probably other languages), you need to have at least a declaration of functions and fields before you can use them:</p>
<pre><code>#include &lt;iostream&gt;

int main(int argc, char **argv) {
  printout();
  return 0;
}

void printout() {
  std::cout &lt;&lt; &quot;TEST&quot; &lt;&lt; std::endl;
}
</code></pre>
<pre><code>% g++ main.cpp 
main.cpp: In function ‘int main(int, char**)’:
main.cpp:4:3: error: ‘printout’ was not declared in this scope; did you mean ‘printf’?
    4 |   printout();
      |   ^~~~~~~~
      |   printf
</code></pre>
<p>Other languages like Java allow you to use methods before declaring them:</p>
<pre><code>public class main {
    public static void main(String[] args) {
    printout();
    }

    private static void printout() {
    System.out.println(&quot;TEST&quot;);
    }
}
</code></pre>
<pre><code>% javac main.java 
% java -cp . main
TEST
</code></pre>
<p>What are the advantages at either compile-time or run-time of requiring functions to be declared <strong>before</strong> they are executed?</p>

<compilers><parsing><functions>
What are the advantages of requiring forward declaration of methods/fields like C/C++ does?
<p>@kaya3's answer is correct but incomplete.</p>
<p>Another advantage of forward declarations is that codegen units (CGUs) can be fully independent. For example, compare C's compilation model to Rust's: in C, having the forward declarations for all dependencies is sufficient to compile the CGU, while in Rust there is no equivalent of forward declarations and the compiler must have access to the implementation of the items as well as the declaration.</p>
<p>This has several consequences:</p>
<ul>
<li>It isn't possible to distribute an object file and link against it; the compiler must have access to the sources (this is also true for C++, whose model is more similar to C, but C++ works around it by allowing you to instantiate templates with specific types in the header itself).</li>
<li>Building code is much less parallel; Rust crates must form a DAG while C's files are massively parallel since each CGU is fully independent from the others</li>
<li>&quot;cyclic imports&quot; are only possible within a CGU. In C it's perfectly possible to have two files which depend on each other's symbols but are compiled independently; in Rust, the requirement for a DAG means that the dependency must be 1-way.</li>
</ul>
<p>The Rust compiler itself has run into these issues, and in order to improve build times  it polyfills forward declarations using function pointers. See <a href="https://rustc-dev-guide.rust-lang.org/query.html#providers" rel="noreferrer">https://rustc-dev-guide.rust-lang.org/query.html#providers</a> for more information.</p>

CC BY-SA 4.0
2023-05-17T06:39:28.047Z
2023-05-17T06:39:28.047Z
jyn
CC BY-SA 4.0
2023-07-19T08:16:50.843Z
I note that Rust allows cyclic dependencies _within_ a crate (ie, library); only inter-library dependencies must form a DAG.
Matthieu M.
CC BY-SA 4.0
2023-07-19T08:19:05.900Z
I am not quite sure about the parallelism argument of C vs Rust. Rust code _can_ be built in parallel too -- that the compiler doesn't is more of a compiler architecture issue. It is _easier_ to parallelize a C build. On the other hand, a parallel C build does _more_ work: each CGU will re-parse every forward declaration.
Matthieu M.
CC BY-SA 4.0
2023-07-20T15:11:22.773Z
@MatthieuM. I think you're talking about parallelism within a single invocation of the compiler. I'm talking about running multiple compiler processes in parallel. parallelism within a single invocation is much more limited and, as you said, harder to implement
jyn
CC BY-SA 4.0
2023-07-20T15:16:05.793Z
I don't know of any _inherent_ limit in internal parallelism, and a straightforward "port" of external parallelism to internal parallelism is no harder to implement either. Compilers with internal parallelism tend to be more complex because they attempt to _capitalize_ on internal parallelization to be smarter and faster, but that's not _inherent_ to using internal parallelization.
Matthieu M.
CC BY-SA 4.0
2023-07-20T15:19:43.100Z
The limitation is that the parallelism is partitioned by the size of the crate. I find it hard to believe that compiling 100 crates in series with internal parallelism would have comparable performance to compiling 100 crates in parallel with a single-threaded compiler.
jyn
CC BY-SA 4.0
2023-07-20T15:25:54.003Z
the fact that in the cargo/rustc toolchain, cargo spawns 1 rustc process per crate, and the rustc process uses _some_ internal parallelism is very specific to cargo/rustc and not an _inherent_ limitation of internal parallelism. If rustc were used as a library in cargo, you could perfectly have internal parallelism _only_. There are good reasons for the architecture choice -- it makes developing cargo and rustc independently from one another possible, crashes can be isolated to a single crate, etc... -- but deep integration would likely be faster...
Matthieu M.
CC BY-SA 4.0
2023-07-20T15:29:24.050Z
agreed, yes — but i don't think there are many languages where the build system and the compiler are combined in that way? the closest i can think of is Java where the package structure means javac sees all packages at once, but i'm not familiar enough to know if that actually allows it to be internally parallel across packages.
jyn
CC BY-SA 4.0
2023-07-20T15:30:15.853Z
i suppose you could consider JavaScript to be compiled by a single internally parallel browser process ... i think Deno does something like that.
jyn
CC BY-SA 4.0
2023-07-21T07:26:25.223Z
I am not super familiar with other languages toolchains, unfortunately. I am not sure if asking for a survey of compiler architectures would be on-topic on this site, though it certainly interests me.
Matthieu M.
CC BY-SA 4.0
2023-05-17T06:43:11.350Z
oh, I forgot to mention: Rust's model has one major advantage that C lacks, which is that the compiler can verify that the types are correct. C is happy to let you declare one type for a function in one CGU and another type for that function in a second CGU, and you won't get an error until the code uses the wrong ABI at runtime.
jyn
<h2>Using the same headers for multiple libraries</h2>
<p>One advantage of requiring forward declarations is that you can separate out the declarations in a separate file, then swap the actual implementation out at will.</p>
<p>Libraries like curses and glulxe are just header files without any code. You can choose from many different implementations depending on your platform, but they use the same header file and so are guaranteed to be compatible with each other.</p>
<p>You can even compile your project to object files using headers only without having any implementation of the library installed, even though you couldn't run it.</p>
<p>In languages with no forward declarations, it's still possible to check if two libraries follow the same interface, but in languages that have headers, you get that feature for free.</p>

CC BY-SA 4.0
2023-05-17T06:36:04.707Z
2023-07-17T19:39:40.037Z
2023-07-17T19:39:40.037Z
mousetail 'he-him'
CC BY-SA 4.0
2023-07-20T15:17:24.120Z
Note this comes with other problems, though - if the header file doesn't match the compiled object file (which can be as subtle as "different -D flags were passed to the preprocessor"), then you can get undefined behavior when your code tries to call it.
jyn
CC BY-SA 4.0
2023-09-08T20:55:40.133Z
@jyn: It's useful for clients of a library to be able to use a brief description of the interface, without having to use the entire source code of the library.  While having a such a digest be a build artifact from compiling the library might be better than having it be a separately maintained source-code file, the risks of such a file becoming out of sync with a compiled library version would seem about the same.
supercat
<h2>Advantage: Code readability</h2>
<p>When everything has to be declared before use, you can read a source file from beginning to end and you'll never encounter a name you don't know. There's a reason why mathematical texts are usually written this way.</p>

CC BY-SA 4.0
2023-07-19T07:43:00.713Z
2023-07-19T07:43:00.713Z
xigoi
CC BY-SA 4.0
2023-07-20T15:14:20.523Z
I don't think this is true in practice since forward declarations are usually separated into their own header file.
jyn
CC BY-SA 4.0
2023-07-20T21:56:23.500Z
@jyn Header files are only really a thing in C and C++.
xigoi
<p><strong>Pro</strong>: Header files provide succinct documentation for the code, compared to the implementation.</p>
<p>Because header files contain only the forward declarations they are far easier to scan through and read than code where the definitions and declarations are unified.</p>

CC BY-SA 4.0
2023-07-18T11:59:57.757Z
2023-07-18T11:59:57.757Z
Jack Aidley
CC BY-SA 4.0
2023-07-20T15:15:33.123Z
I agree this is a benefit. You can get this back with sufficiently good tooling, though — Doxygen and Rustdoc both generate summaries like you're talking about.
jyn
CC BY-SA 4.0
2023-07-20T15:54:14.740Z
@jyn: Sure, and many minor languages benefits can be matched by good tooling. Although I will say I have pretty negative experiences with Doxygen (not used Rustdoc), IDE navigation tools also do help but I still find them less useful than header files. That said, I still wouldn't choose header files over other options because IMO the downsides outweigh the benefits.
Jack Aidley
<p>There is only really one advantage ─ it means that name resolution can be done in a single pass, because names cannot be used before they are declared.</p>
<p>This was more important in the olden days, when programming language design was much more limited by the capabilities of the computers that the compilers had to run on. By requiring forward declarations, one pass was enough to both build a symbol table (i.e. a mapping of names to declarations) and also resolve names to symbols (i.e. when <code>x</code> occurs in an expression, find which declaration of <code>x</code> that name refers to). Then at the end you just needed to check that there were no missing declarations (i.e. forward declarations which weren't later &quot;filled out&quot; with proper declarations).</p>
<p>Nowadays there is no need for this, but you might still do it if the simplicity of implementing your compiler is more important to you than the ease of use of your language; for example, if your compiler is just a hobby project, or you are compiling source code generated programmatically rather than written by humans.</p>

CC BY-SA 4.0
2023-05-16T21:05:05.357Z
2023-05-16T21:05:05.357Z
kaya3
CC BY-SA 4.0
2023-05-20T00:18:38.490Z
related: Cppfront doesn't require declare-before-use. It generates forward-declarations in the transpiled C++ source.
starball
# What are the advantages of requiring forward declaration of methods/fields like C/C++ does?
Posted by: username_0 on 2023-05-16
In C and C++ (and probably other languages), you need to have at least a declaration of functions and fields before you can use them:
#include 

int main(int argc, char **argv) {
  printout();
  return 0;
}

void printout() {
  std::cout << "TEST" << std::endl;
}

% g++ main.cpp 
main.cpp: In function ‘int main(int, char**)’:
main.cpp:4:3: error: ‘printout’ was not declared in this scope; did you mean ‘printf’?
    4 |   printout();
      |   ^~~~~~~~
      |   printf

Other languages like Java allow you to use methods before declaring them:
public class main {
    public static void main(String[] args) {
    printout();
    }

    private static void printout() {
    System.out.println("TEST");
    }
}

% javac main.java 
% java -cp . main
TEST

What are the advantages at either compile-time or run-time of requiring functions to be declared before they are executed?
**Tags:** compilers parsing functions
---
## 5 answers
### Answer by username_7 on 2023-05-16. Score: 22 | Accepted answer
There is only really one advantage ─ it means that name resolution can be done in a single pass, because names cannot be used before they are declared.

This was more important in the olden days, when programming language design was much more limited by the capabilities of the computers that the compilers had to run on. By requiring forward declarations, one pass was enough to both build a symbol table (i.e. a mapping of names to declarations) and also resolve names to symbols (i.e. when `x` occurs in an expression, find which declaration of `x` that name refers to). Then at the end you just needed to check that there were no missing declarations (i.e. forward declarations which weren't later "filled out" with proper declarations).

Nowadays there is no need for this, but you might still do it if the simplicity of implementing your compiler is more important to you than the ease of use of your language; for example, if your compiler is just a hobby project, or you are compiling source code generated programmatically rather than written by humans.
> **Comments:**
> **username_8** (2023-05-20): related: Cppfront doesn't require declare-before-use. It generates forward-declarations in the transpiled C++ source.  
---
### Answer by username_1 on 2023-05-17. Score: 11
@kaya3's answer is correct but incomplete.

Another advantage of forward declarations is that codegen units (CGUs) can be fully independent. For example, compare C's compilation model to Rust's: in C, having the forward declarations for all dependencies is sufficient to compile the CGU, while in Rust there is no equivalent of forward declarations and the compiler must have access to the implementation of the items as well as the declaration.

This has several consequences:

* It isn't possible to distribute an object file and link against it; the compiler must have access to the sources (this is also true for C++, whose model is more similar to C, but C++ works around it by allowing you to instantiate templates with specific types in the header itself).
* Building code is much less parallel; Rust crates must form a DAG while C's files are massively parallel since each CGU is fully independent from the others
* "cyclic imports" are only possible within a CGU. In C it's perfectly possible to have two files which depend on each other's symbols but are compiled independently; in Rust, the requirement for a DAG means that the dependency must be 1-way.

The Rust compiler itself has run into these issues, and in order to improve build times it polyfills forward declarations using function pointers. See <https://rustc-dev-guide.rust-lang.org/query.html#providers> for more information.
> **Comments:**
> **username_1** (2023-05-17): oh, I forgot to mention: Rust's model has one major advantage that C lacks, which is that the compiler can verify that the types are correct. C is happy to let you declare one type for a function in one CGU and another type for that function in a second CGU, and you won't get an error until the code uses the wrong ABI at runtime.  
> **username_2** (2023-07-19): I note that Rust allows cyclic dependencies _within_ a crate (ie, library); only inter-library dependencies must form a DAG.  
> **username_2** (2023-07-19): I am not quite sure about the parallelism argument of C vs Rust. Rust code _can_ be built in parallel too -- that the compiler doesn't is more of a compiler architecture issue. It is _easier_ to parallelize a C build. On the other hand, a parallel C build does _more_ work: each CGU will re-parse every forward declaration.  
> **username_1** (2023-07-20): @MatthieuM. I think you're talking about parallelism within a single invocation of the compiler. I'm talking about running multiple compiler processes in parallel. parallelism within a single invocation is much more limited and, as you said, harder to implement  
> **username_2** (2023-07-20): I don't know of any _inherent_ limit in internal parallelism, and a straightforward "port" of external parallelism to internal parallelism is no harder to implement either. Compilers with internal parallelism tend to be more complex because they attempt to _capitalize_ on internal parallelization to be smarter and faster, but that's not _inherent_ to using internal parallelization.  
> **username_1** (2023-07-20): The limitation is that the parallelism is partitioned by the size of the crate. I find it hard to believe that compiling 100 crates in series with internal parallelism would have comparable performance to compiling 100 crates in parallel with a single-threaded compiler.  
> **username_2** (2023-07-20): the fact that in the cargo/rustc toolchain, cargo spawns 1 rustc process per crate, and the rustc process uses _some_ internal parallelism is very specific to cargo/rustc and not an _inherent_ limitation of internal parallelism. If rustc were used as a library in cargo, you could perfectly have internal parallelism _only_. There are good reasons for the architecture choice -- it makes developing cargo and rustc independently from one another possible, crashes can be isolated to a single crate, etc... -- but deep integration would likely be faster...  
> **username_1** (2023-07-20): agreed, yes — but i don't think there are many languages where the build system and the compiler are combined in that way? the closest i can think of is Java where the package structure means javac sees all packages at once, but i'm not familiar enough to know if that actually allows it to be internally parallel across packages.  
> **username_1** (2023-07-20): i suppose you could consider JavaScript to be compiled by a single internally parallel browser process ... i think Deno does something like that.  
> **username_2** (2023-07-21): I am not super familiar with other languages toolchains, unfortunately. I am not sure if asking for a survey of compiler architectures would be on-topic on this site, though it certainly interests me.  
---
### Answer by username_3 on 2023-05-17. Score: 8
## Using the same headers for multiple libraries

One advantage of requiring forward declarations is that you can separate out the declarations in a separate file, then swap the actual implementation out at will.

Libraries like curses and glulxe are just header files without any code. You can choose from many different implementations depending on your platform, but they use the same header file and so are guaranteed to be compatible with each other.

You can even compile your project to object files using headers only without having any implementation of the library installed, even though you couldn't run it.

In languages with no forward declarations, it's still possible to check if two libraries follow the same interface, but in languages that have headers, you get that feature for free.
> **Comments:**
> **username_1** (2023-07-20): Note this comes with other problems, though - if the header file doesn't match the compiled object file (which can be as subtle as "different -D flags were passed to the preprocessor"), then you can get undefined behavior when your code tries to call it.  
> **username_4** (2023-09-08): @jyn: It's useful for clients of a library to be able to use a brief description of the interface, without having to use the entire source code of the library.  While having a such a digest be a build artifact from compiling the library might be better than having it be a separately maintained source-code file, the risks of such a file becoming out of sync with a compiled library version would seem about the same.  
---
### Answer by username_6 on 2023-07-18. Score: 3
**Pro**: Header files provide succinct documentation for the code, compared to the implementation.

Because header files contain only the forward declarations they are far easier to scan through and read than code where the definitions and declarations are unified.
> **Comments:**
> **username_1** (2023-07-20): I agree this is a benefit. You can get this back with sufficiently good tooling, though — Doxygen and Rustdoc both generate summaries like you're talking about.  
> **username_6** (2023-07-20): @jyn: Sure, and many minor languages benefits can be matched by good tooling. Although I will say I have pretty negative experiences with Doxygen (not used Rustdoc), IDE navigation tools also do help but I still find them less useful than header files. That said, I still wouldn't choose header files over other options because IMO the downsides outweigh the benefits.  
---
### Answer by username_5 on 2023-07-19. Score: 2
## Advantage: Code readability

When everything has to be declared before use, you can read a source file from beginning to end and you'll never encounter a name you don't know. There's a reason why mathematical texts are usually written this way.
> **Comments:**
> **username_1** (2023-07-20): I don't think this is true in practice since forward declarations are usually separated into their own header file.  
> **username_5** (2023-07-20): @jyn Header files are only really a thing in C and C++.  
---
