Archived

This forum has been archived. Please start a new discussion on GitHub.

slice2cpp & --include-dir

--include-dir seems to have no effect on the resulting cpp/h code, save of changing the name of the #ifndef/#define guard in the header :)

Given a path like shared/slice/A.ice and shared/slice/B.ice, where B.ice #include's A.ice, with 1.2.0, the following give different results:

From root directory:

vladimir@river[1084]% slice2cpp --output-dir shared shared/slice/*.ice
vladimir@river[1085]% grep A.h shared/B.h
#include <shared/slice/A.h>

That one I can live with; it's not ideal, as I think it should default to the "output-dir", but it's understandable.

vladimir@river[1086]% slice2cpp --output-dir shared --include-dir shared shared/slice/*.ice
vladimir@river[1087]% grep A.h shared/B.h
#include <shared/slice/A.h>

I don't understand why it's not <shared/A.h> in the above.

Now I change into the "shared" directory:

vladimir@river[1101]% slice2cpp --output-dir ../shared ../shared/slice/*.ice
vladimir@river[1102]% grep A.h B.h
#include <../shared/slice/A.h>

vladimir@river[1103]% slice2cpp --output-dir ../shared -I../shared/slice ../shared/slice/*.ice
vladimir@river[1104]% grep A.h B.h
#include <A.h>

Those two have me baffled (though the latter is the behaviour I want!) :) slice2cpp seems to be checking the include paths specified, and isn't outputting the include directory prefix if it can find an exact match?

It seems that --include-dir was created to solve this problem, but maybe I'm misunderstanding its use. I'd ideally like to be able to say "don't put any prefixes in #include directives, output just the filename", and let me deal with the compiler search path later on.

Comments

  • mes
    mes California
    A lot of careful consideration has been invested in the C++ code generator's support for header file inclusion. As usual, our goal is to make the simple cases easy while not precluding more complex scenarios. The command-line options supported by the code generator are sufficient for building Ice, which I think most people would admit is a non-trivial example. :)

    That being said, we recognize that it can be confusing to get things configured correctly, so we'd like to explain how it works here, and then include this explanation in the manual. Please let us know if anything is unclear.

    Thanks,
    - Mark


    The #include directives generated by the Slice-to-C++ compiler can be a source of confusion if the semantics governing their generation are not well-understood.

    Given that the #include directives in header files and source files are generated using different semantics, we describe them in separate sections below. First, however, let us review the relevant translator options:
    • -I - Adds an include path for use in locating included files. Also used to influence #include directives in header (.h) files.
    • --include-dir - Modifies the #include directives generated in source (.cpp) files. This option has no affect on header files.
    • --output-dir - Directs the translator to place all generated files in the specified directory. This option has no material impact on the contents of the generated code.

    The use of the -I and --include-dir options is discussed in more detail in the following sections.

    Header Files

    In most cases, the compiler generates the appropriate #include directives by default.

    As an example, suppose file A.ice includes B.ice using the following statement:
    // A.ice
    #include <B.ice>
    
    Assuming both files are in the current working directory, we run the compiler as shown below:
    $ slice2cpp -I. A.ice
    
    And the compiler generates this #include directive in A.h:
    // A.h
    #include <B.h>
    
    If the proper include paths are specified to the C++ compiler, everything should compile correctly.

    Similarly, consider the common case where A.ice includes B.ice from a subdirectory:
    // A.ice
    #include <inc/B.ice>
    
    Assuming both files are in the inc subdirectory, we run the compiler as shown below:
    $ slice2cpp -I. inc/A.ice
    
    The default output of the compiler produces this #include directive in A.h:
    // A.h
    #include <inc/B.h>
    
    Again, it is the user's responsibility to ensure that the C++ compiler is configured to find inc/B.h during compilation.

    Now let us consider a more complex example, in which we do not want the #include directive in the header file to match that of the Slice file. This can be necessary when the organizational structure of the Slice files does not match the application's C++ code. In such a case, the user may need to relocate the generated files from the directory in which they were created, and the #include directives must be aligned with the new structure.

    For example, let us assume that B.ice is located in the subdirectory slice/inc:
    // A.ice
    #include <slice/inc/B.ice>
    
    However, we do not want the slice subdirectory to appear in the #include directive generated in the header file, therefore we specify the additional compiler option -Islice:
    $ slice2cpp -I. -Islice slice/inc/A.ice
    
    The generated code demonstrates the impact of this extra option:
    // A.h
    #include <inc/B.h>
    
    As you can see, the #include directives generated in header files are affected by the include paths that you specify when running the compiler. Specifically, the include paths are used to abbreviate the pathname in generated #include directives.

    When translating an #include directive from a Slice file to a header file, the compiler compares each of the include paths against the path of the included file. If an include path matches the leading portion of the included file, the compiler removes that leading portion when generating the #include directive in the header file. If more than one include path matches, the compiler selects the one that results in the shortest path for the included file.

    For example, suppose we had used the following options when compiling A.ice:
    $ slice2cpp -I. -Islice -Islice/inc slice/inc/A.ice
    
    In this case, the compiler compares all of the include paths against the included file slice/inc/B.ice and generates the following directive:
    // A.h
    #include <B.h>
    
    The option -Islice/inc produces the shortest result, therefore the default path for the included file (slice/inc/B.h) is replaced with B.h.

    In general, the -I option plays two roles: it enables the preprocessor to locate included Slice files, and it provides you with a certain amount of control over the generated #include directives. In the last example above, the preprocessor locates slice/inc/B.ice using the include path specified by the -I. option. The remaining -I options do not help the preprocessor locate included files; they are simply hints to the compiler.

    Finally, we recommend using caution when specifying include paths. If the preprocessor is able to locate an included file via multiple include paths, it always uses the first include path that successfully locates the file. If you intend to modify the generated #include directives by specifying extra -I options, you must ensure that your include path hints match the include path selected by the preprocessor to locate the included file. As a general rule, you should avoid specifying include paths that enable the preprocessor to locate a file in multiple ways.

    Source Files

    By default, the compiler generates #include directives in source files using only the base name of the included file. This behavior is usually appropriate when the source file and header file reside in the same directory.

    For example, suppose A.ice includes B.ice from a subdirectory, as shown in the following snippet of A.ice:
    // A.ice
    #include <inc/B.ice>
    
    We generate the source file using this command:
    $ slice2cpp -I. inc/A.ice
    
    Upon examination, we see that the source file contains the following #include directive:
    // A.cpp
    #include <B.h>
    
    However, suppose that we wish to enforce a particular standard for generated #include directives so that they are compatible with our C++ compiler's existing include path settings. In this case, we use the --include-dir option to modify the generated code. For example, consider the compiler command shown below:
    $ slice2cpp --include-dir src -I. inc/A.ice
    
    The source file now contains the following #include directive:
    // A.cpp
    #include <src/B.h>
    
    Any leading path in the included file is discarded as usual, and the value of the --include-dir option is prepended.
  • Ah! Thank you, things are much clearer now. The --include-dir option was confusing me the most... I'd suggest adding "in the source" to the end of the description for --include-dir in the slice2cpp -h output.

    Thanks!
    - Vlad