Archived

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

Why does slice2cpp use #include <foo.h> instead of "foo.h"?

The C++ files generated by slice2cpp (.cpp and .h files) refer to internal header files using the system/library includes (#include <foo.h>) instead of the user-level includes (#include "foo.h"). Why is this?

It makes it weird trying to compile the generated C++ code (you need to specify the local directory as an include location). Also, if you use --include-dir to put the header files into a subdirectory, then you need to specify that subdirectory as an include directory as well.

Comments

  • Sorry, I meant to ask if this was related to issue 3871 (where it appears slice2cpp has trouble distinguishing between includes of standard Ice header files, and of locally generated files)?

    http://www.zeroc.com/forums/help-center/3871-slice2cpp-header-renaming-header-ext.html
  • Hi Gary,

    No, this is not a bug. I can't answer why they chose this route, but I'm sure the Ice guys have a good reason.

    If you take a look at their FAQ regarding setting up a Visual Studio project, they explicitly say that you must include . since this is where the generated files are place. I'm not sure if you're using VS, but their FAQ can be easily applied to other IDEs.

    ZeroC - How do I create a Visual Studio project for Ice?

    Pete
  • Thanks Pete,

    Yeah, I figured that part out and got everything compiled. But then I was twisting my brain trying to understand why it was done this way (it's been a while since I last looked at C++, so I was thinking it must have been some subtlety I had forgotten).

    gary
  • matthew
    matthew NL, Canada
    Using double quotes is a way of adding . (the current working directory) to the include search path. If double quotes were used, once generated this choice is embedded in the code for good. However, by using angle brackets its left to the build system whether to decide whether to add . to the search path or not.
  • Sorry, I'm blathering, and this isn't a particularly important topic, so feel free to ignore this thread.

    My understanding is that a double quoted #include is a way of specifying includes relative to the directory of the file currently being parsed. This is subtly different from the current working directory (since the file currently being parsed may be a #include'd file in a different directory). So the effect is that if you use a double quoted include, you are specifying a file relative to whatever file you are currently coding. AFAIK this is not something you can specify to most C compilers

    As a coding style, it also has the advantage that it is easy to distinguish between your header files and those of a library.

    It also seems to be a widespread practice to use double-quoted includes when referring to header files within your own library/program (eg. as recommended by the CERN C++ coding standard, criterion CP3) ??
  • matthew
    matthew NL, Canada
    The handling of quotes by the C preprocessor has historically not been very standard between various operating systems. With angle brackets you always have precise control over the location from where the header file is loaded.

    With respect to "your source code" vs "library source code" generated code could be either (and often changes roles as time goes on), so the choice of angle brackets is the most appropriate.
  • Hmmm, I hear what you're saying, but I have to respectfully disagree. However, there doesn't seem to be much prospect of resolving our different opinions in a forum, so I am happy to let the matter rest.

    But I do still want to find out how you *expect* slice2cpp to be used. In particular, what is the --include-dir option for? Taking the (slightly simplified) example from section 6.15.1 of the documentation, suppose we have:
    //A.ice
    #include "B.ice"

    //B.ice

    ...which we compile using...
    % slice2cpp --include-dir src A.ice B.ice

    ...then we get...
    //A.cpp
    #include <src/A.h>

    //A.h
    #include <B.h>

    //B.cpp
    #include <src/B.h>

    ...where all the .h and .cpp files are generated into the same directory. How would you expect the .cpp and .h files to be subsequently arranged, and what include directories would you explicitly specify when you compile A.cpp?
  • Hi Gary,

    I'm not sure what you're disagreeing with. After reviewing Matthew's explanation, the problems with using quotes are quite obvious.

    The --include-dir option is used if you wish to separate header and implementation files. I think it's pretty obvious based on your sample output, but you would need the source files in (.) and the header files in (./src). You would need to specify the include directory (./src) to your c++ compiler.

    Pete
  • slypete wrote: »
    You would need to specify the include directory (./src) to your c++ compiler.

    You would need to specify the include directory (.) to your c++ compiler.
  • slypete wrote: »
    You would need to specify the include directory (.) to your c++ compiler.

    AFAIK you would need to specify both. You need "." so that A.cpp can find <src/A.h>, and you need "src" so that A.h (as included by A.cpp) can find <B.h>
  • matthew wrote: »

    Yes, but it doesn't seem to explain why the generated header files don't have the subdirectory as part of their includes.
  • Hi Gary,

    That option is for separating header and source files. It only modifies source files for that reason. That option in the manual is only underthe source files subsection.

    Pete
  • GaryD wrote: »
    AFAIK you would need to specify both. You need "." so that A.cpp can find <src/A.h>, and you need "src" so that A.h (as included by A.cpp) can find <B.h>

    I drew this picture to get a better visual of your file system:
    .
       A.cpp  (#include <src/A.h>)
       B.cpp  (#include <src/B.h>)
    
    ./src
       A.h (#include <B.h>)
       B.h
    

    So yes, you are correct. Both are required to compile this.

    Pete
  • slypete wrote: »
    I drew this picture to get a better visual of your file system:
    .
       A.cpp  (#include <src/A.h>)
       B.cpp  (#include <src/B.h>)
    
    ./src
       A.h (#include <B.h>)
       B.h
    

    So yes, you are correct. Both are required to compile this.

    Pete

    Yes, sorry, I should have done such a diagram myself to make things clearer.

    So on Pete's diagram above, you have to include the following directories in the include search path for your compiler:
    * . -> so that we can find <src/A.h> and <src/B.h> included from the .cpp files
    * ./src -> so that we can find <B.h> included from A.h

    If we hadn't used the --include-dir option (but still put our generated files into the same directory structure), then we would have:
    .
       A.cpp  (#include <A.h>)
       B.cpp  (#include <B.h>)
    
    ./src
       A.h (#include <B.h>)
       B.h
    

    So in this scenario, you have to include the following directories in the include search path for your compiler:
    * ./src -> so that we can find <A.h> and <B.h>
    * . -> this might possibly already be in your search path (since that was the premise).

    So my question is, what is the point of using --include-dir? Comparing the first scenario (where we used --include-dir) with the second (where we didn't), you still have to specify `./src' on the search path for your compiler; you still have to move the header files into the src directory yourself; and you still have references to `#include <B.h>'. I don't see what we have gained.

    Is there a different scenario where it would be more useful?
  • --include-dir is useful if your Slice files reside in directories that correspond to a prefix. For example, for Ice, we have all the slice files in slice/Ice, slice/Freeze, slice/Glacier2, and so on.

    The header files for these are in a corresponding hierarchy: include/Ice, include/Freeze, include/Glacier2, and so on.

    If one my Slice files includes one of the Ice-supplied Slice files, I do it like so:
    // MyDefs.ice
    #include <Ice/BuiltinSequences.ice>
    

    When compiling this definition, I use:
    slice2cpp -I/opt/Ice-3.3/slice MyDefs.ice
    

    The -I option tells the compiler where it can find Ice/BuiltinSequences.ice.

    With the preceding slice2cpp command, MyDefs.cpp contains:
    // MyDefs.cpp
    #include <MyDefs.h>
    

    However, your build environment may actually not leave the generated header in the current directory but move it elsewhere (just as we do with the Ice headers). For example, you may copy all the headers into a directory MyHeaders. So, you can use:
    slice2cpp --include-dir MyHeaders -I/opt/Ice-3.3/slice MyDefs.ice
    

    With this, the generated MyDefs.cpp now contains:
    // MyDefs.cpp
    #include <MyHeaders/MyDefs.h>
    

    Comparing the first and the second version, you find that, either way, you only need to specify -I. for the C++ compiler (assuming that MyHeaders is a subdir of .).

    Just as with Ice, you can use this to have module prefixes in the generated headers, so you need to point the C++ compiler at only the top-level directory. For example,
    slice2cpp --include-dir ModuleA ModuleA.ice
    slice2cpp --include-dir ModuleB ModuleB.ice
    

    If you have a directory structure such as Modules/ModuleA and Modules/ModuleB, -IModules for the C++ compiler then causes all the headers to be found where they are.

    Cheers,

    Michi.
  • Thanks Michi, that's what I thought the intention was (and how I had been planning to use it). This way you can enforce some structure by putting your header files into specific subdirectories, and the generated code allows for that without the need for any editing.

    However, the way the option works at the moment does not seem to allow very well for slice files that include other slice files.

    My question is why the generated header files do not have the module prefix (when referring to other generated files)?

    In the example that Pete and myself were discussing, you end up with one generated header file referring to another one. Given that we are using <> includes (and without going back into that discussion), this means that the seach path for includes must explicitly specify all the subdirectories, as well as the top directory.

    Referring to your final example, if ModuleA.ice also includes ModuleB.ice, you would have to specify `-IModules -IModules/ModuleB' to your C++ compiler. This seems, at the very least, somewhat untidy.
  • I'm not sure I understand what you mean here. Each include directive can simply use the prefix. For an example, have a look at the IceGrid Slice files. They consistently use directives such as
    #include <Ice/Identity.ice>
    #include <Ice/BuiltinSequences.ice>
    #include <Glacier2/Session.ice>
    #include <IceGrid/Exception.ice>
    

    That way things are nice and neat, and a single -I option for each module does the job.

    Am I missing something else? If so, could you post a self-contained example that demonstrates the problem?

    Cheers,

    Michi.
  • Hi Michi - please see the attached "slice2cpp-include-example.zip".

    So our scenario is as follows:
    * We have multiple Slice files
    * One of our slice files will be included by the others (eg. say it defines some common data structures or exceptions that are common across the whole package).
    * We want the output to be in the `src' subdirectory

    So if I now run...
    % slice2cpp -I. A.ice B.ice --include-dir src
    

    I then take the generated C++ header files and put them in the src subdirectory. This file structure is what you find in the attached zipfile.

    Now, in the file ./A.cpp, we have
    #include <src/A.h>
    

    And in the file ./src/A.h we have
    #include <B.h>
    

    When i go to compile this I need to explicitly tell my C++ compiler to look in both `.' and `src' for include files. eg
    g++ -o test.exe A.cpp B.cpp -I. -Isrc ...
    

    So if I am going to do this, then what's the point of having `src' as a separate subdirectory for the include tree? The --include-dir option does not have any benefit for me when I go to compile my C++.

    Do you see what I am saying?
  • matthew
    matthew NL, Canada
    The source of the issue is that you do
    #include <B.ice>
    

    If instead you
    #include <src/B.ice>
    

    then this issue would not exist. Does B.ice actually reside in the src directory? If so, then you can use -I.. when building A.ice and all would be well. If not, then perhaps you should rearrange your build system :)
  • Ack! I don't feel like we're getting anywhere with this discussion. Let me try to summarise...

    The problems I have are:
    1) Ice avoids the use of double-quoted includes. The "some operating systems do weird things" argument is unconvincing.
    2) There does not appear to be any scenario where slice2cpp --include-dir actually provides any benefit. This is partly due to point 1, and partly due to it only affecting the generated .cpp files. We have discussed a large number of scenarios, and they either don't use --include-dir, or are't improved by the use of --include-dir

    Now, that's all a bit theoretical, so here's a real-life problem that needs to be solved:
    3) As a consequence of points 1 and 2, slice2cpp pollutes the namespace for library header files.

    I probably need to flesh out issue #3. Imagine that you are creating a moderately sized new software project. You want to define different parts of the interface in different Slice files (because that makes it more manageable). Some common definitions (eg. data types, standard exceptions, etc.) will be in separate Slice files (eg. types.ice, exceptions.ice, ...), that get included by the other files.

    Now, each Slice file gets turned into a .cpp and a .h file, which get the same name as the corresponding Slice file. And if a Slice file includes another Slice file, then the corresponding header also includes the other header. So one header file might look like:
    // myserver.h
    #include <exceptions.h> // Is meant to refer to the file generated from our package's exceptions.ice
    #include <types.h> // Is meant to refer to the file generated from our package's types.ice
    
    ...which is obviously problematic.

    So, how do we deal with the fact that the .h files generated for our package look like standard system header files?

    Some possible solutions:
    a) Use double-quoted includes to refer to generated files within our package. But this can't be done because of issue #1 above.
    b) Add an option to slice2cpp that prepends a library name to all the includes within this package. But the existing --include-dir option doesn't actually work - see issue #2 above.
    c) Be careful how you name your Slice files. With some trial and error, this will probably work. However, it is asking for trouble in the future - eg. if you port your package to a different system, or install more libraries.
    d) Matthew's suggestion above. Change all your package's Slice files to include each other with the directory prepended. This works, but it's hacky, and requires you to name the directory for your slice files according to the package name (which is a bit unrealistic for a large project, but manageable)

    Now, does this all make sense? Do you understand the problems I am describing (I am not asking you to agree with them, but right now we don't seem to even know what we are discussing)? Is there another way to solve issue #3?
  • GaryD wrote: »
    c) Be careful how you name your Slice files. With some trial and error, this will probably work. However, it is asking for trouble in the future - eg. if you port your package to a different system, or install more libraries.
    d) Matthew's suggestion above. Change all your package's Slice files to include each other with the directory prepended. This works, but it's hacky, and requires you to name the directory for your slice files according to the package name (which is a bit unrealistic for a large project, but manageable)

    I think you came up with two solutions yourself right here. Simply capitalizing the first character of your slice files solves virtually all of the problems in solution 3c. Furthermore, Matthew's 3d is even more flawless.

    As you can see, this is a non-issue. I'm unsure what all of the fuss is about. It appears that you are searching for an option to prefix directories to include directives; however, ZeroC has already provided a way to do this.

    Pete
  • matthew
    matthew NL, Canada
    GaryD wrote: »
    Ack! I don't feel like we're getting anywhere with this discussion. Let me try to summarise...

    The problems I have are:
    1) Ice avoids the use of double-quoted includes. The "some operating systems do weird things" argument is unconvincing.

    Ice itself is operating system independent. Therefore we avoid the use of constructs that are don't behave the same across various operating systems. With respect to single quotes Windows & Unix do not behave the same. You also must consider that there are things you must be aware of. The semantics of the slice compiler, and the semantics of the C++ compiler. They may not be the same, because they use different C preprocessors. Furthermore, the C preprocess that the slice compiler has changed over time from the GNU one, to mcpp. Imagine if the semantics of your project that has worked since day one suddenly changes because the slice C preprocessor has changed. Not good! Avoiding the use of single quotes avoids this issue altogether.
    2) There does not appear to be any scenario where slice2cpp --include-dir actually provides any benefit. This is partly due to point 1, and partly due to it only affecting the generated .cpp files. We have discussed a large number of scenarios, and they either don't use --include-dir, or are't improved by the use of --include-dir

    Ice itself uses --include-dir extensively in its build system, and it could not work without it. I'm afraid, that your suggestion that --include-dir affect the generated header files would not work in the general case.

    In summary, the -I directives to the slice compiler affects the generated set of #include directives in the header file. The --include-dir directive affects only the source file.
    ....

    Now, does this all make sense? Do you understand the problems I am describing (I am not asking you to agree with them, but right now we don't seem to even know what we are discussing)? Is there another way to solve issue #3?

    I understand the situation that you have described, and have explained a solution which, unfortunately, you don't seem to like.

    Ice is a large project, and Ice itself has all of the issues you have described above, and furthermore Ice solves them by using the solution that I described. Before we have any further discussion, can you review the way that Ice itself is packaged and built? I think the way we arrange things makes alot of sense, and is quite simple and easy to understand.
  • I think I understand the point GaryD is making, and I posted about a similar issue a while ago here

    A colleague of mine came up with a nice cmake solution for running slice2cpp in our project, but it still seems very unintuitive that the #include statements in generated .cpp files work differently than the #include statements in generated .h file.

    MEF
  • Right, I will run with that suggestion for using slice2cpp, and also take a look at the way ICE is built.

    I did not mean to be rude, so I am sorry if I came across that way. The ZeroC team has obviously done a great job building ICE. I am struggling to understand the reasons why some things are done the way they are done (or even to communicate those things, it seems). But that's by the by - let's just move on, shall we.