"Using" directives inside classes

Following my previous question. I've settled on using using directives to alias types inside my classes to avoid importing other stuff and polluting other headers that use these offending headers.

namespace MyLibrary {
    namespace MyModule1 {
        class MyClass1 {
        public:
            float X;
            float Y;

            MyClass1(float x, float y): X(x), Y(y) {}
        };
    } // namespace MyModule1

    namespace MyModule2 {
        class MyClass2 {
        private:
            // as suggested in linked question
            using MyCustomType1 = MyLibrary::MyModule1::MyClass1;

        public:
            void DoSomething(MyCustomType1 parameter) {
                std::cout << parameter.X << std::endl;
                std::cout << parameter.Y << std::endl;
            }
        };
    } // namespace MyModule2
} // namespace MyLibrary

int main(int argc, char* argv[])
{
    MyLibrary::MyModule1::MyClass1 some_parameter(1.0f, 2.0f);
    MyLibrary::MyModule2::MyClass2 some_var;

    // Can't do this
    // MyLibrary::MyModule2::MyClass2::MyCustomType1 some_other_var;

    // But I can do this
    some_var.DoSomething(some_parameter);
    return 0;
}

How will the users outside of the MyLibrary namespace know what is MyCustomType1 if it is aliased inside a class (privately)?

Is my usage here of using legal, or is this a dirty hack I'm accidentally doing?

Upvotes: 0

Views: 77

Answers (1)

Sam Varshavchik
Sam Varshavchik

Reputation: 118352

They will know for the simple reason you have to #include the declarations of both classes.

Having read this, and the previous question, I think the missing concept here is the concept of forward declarations.

Consider the following header file, let's called this file mymodule1_fwd.H:

namespace MyLibrary {
    namespace MyModule1 {
        class MyClass1;
    } // namespace MyModule1
}

That's it. This is sufficient for you to declare MyClass2:

#include "mymodule1_fwd.H"

namespace MyModule2 {
    class MyClass2 {
    private:
        // as suggested in linked question
        using MyCustomType1 = MyLibrary::MyModule1::MyClass1;

    public:
        void DoSomething(MyCustomType1 parameter);
    };
} // namespace MyModule2

Note that including this header file only will not really automatically get the entire MyModule class declaration. Also note the following:

You can't define the contents of the inline DoSomething() class method, because it actually uses the aliased type. This has the following consequences:

  • You have to define the DoSomething() method somewhere, in some way, probably inside the .C implementation translation module.

  • Similarly, you have to have declare the actual MyClass1 class from the mymodule1_fwd.H header file. I am using my own personal naming convention here, "filename_fwd.H" for forward declarations, the forward declaration header file; and "filename.H" for the actual class implementation, the implementation header file.

  • Callers of the DoSomething() method will have to explicitly #include the actual class declaration header file for MyClass, since they have to pass it as a parameter.

You can't really avoid the fact that the callers have to know the class that they're actually using to pass parameters. But only the callers of the DoSomething() method will need that. Something that uses other parts of the MyClass2, and don't invoke DoSomething(), don't need to know anything about MyClass1, and the actual class declaration won't be visible to them unless they explicitly #include the class implementation header file.

Now, if you still need DoSomething() to be inlined, for performance reasons, there are a couple of tricks that can be used, with preprocessor directives, that if someone #includes all the necessary header files, they'll get the inlined declaration of the DoSomething() method.

But that'll have to be another question.

Upvotes: 1

Related Questions