Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Special Interest Groups
  3. C++ Gurus
  4. template/macro for reducing redundant functions?
Forum Updated to NodeBB v4.3 + New Features

template/macro for reducing redundant functions?

Scheduled Pinned Locked Moved Solved C++ Gurus
10 Posts 3 Posters 1.3k Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • fcarneyF Offline
    fcarneyF Offline
    fcarney
    wrote on last edited by fcarney
    #1

    This is a simple example, but it basically is a bunch of redundant class methods:

    class JMsg {
    ...
    JMsg& arg(bool arg){
            m_args.append(QJsonValue(arg));
            return *this;
        }
        JMsg& arg(int arg){
            m_args.append(QJsonValue(arg));
            return *this;
        }
        JMsg& arg(double arg){
            m_args.append(QJsonValue(arg));
            return *this;
        }
        JMsg& arg(const QString arg){
            m_args.append(QJsonValue(arg));
            return *this;
        }
        JMsg& arg(const char* arg){
            m_args.append(QJsonValue(arg));
            return *this;
        }    
    ...
    };
    

    It feels like a candidate for templating, but all I could think of was this:

    template<class T>
    JMsg& arg(T arg){
            m_args.append(QJsonValue(arg));
            return *this;
        }
    

    But the code to use it is ugly:

    JMsg().arg<int>().arg<float>()
    

    Is there a way to make the template private and declare a member function?

    class JMsg {
    private:
    template<class T>
    JMsg& arg(T arg){
            m_args.append(QJsonValue(arg));
            return *this;
        }
    public:
    arg<int>;  // I know this won't work, but it would nicer if it could create members this way.
    };
    

    My other thought was using a macro, but that gets ugly too, but at least it can create class members.

    #define JMSG_ARG(atype) \
    JMsg& arg(atype arg){ \
        m_args.append(QJsonValue(arg)); \
        return *this; \
    } \
    ...
    JMSG_ARG(float)
    

    Like I said, macros are ugly but effective. I hope I am just completely misunderstanding how templates can be used.

    C++ is a perfectly valid school of magic.

    1 Reply Last reply
    0
    • fcarneyF Offline
      fcarneyF Offline
      fcarney
      wrote on last edited by
      #2

      Why do I always find the answer after I post...

      class JMsg{
      ...
      public:
          template<class T>
          JMsg& arg(T arg){
              m_args.append(QJsonValue(arg));
              return *this;
          }
      ...
      };
      
      template JMsg& JMsg::arg<bool>(bool arg);
      template JMsg& JMsg::arg<int>(int arg);
      

      What I don't like is that the template function is public. I don't want another programmer to define arbitrary types to use with the class. The ones selected are the ones I want to support. I cannot define the template with a private version. I tried making it a friend template, but then it cannot access members like it did before. I am not processing the object passed to the function, so I guess that makes sense. If there were a member function version of friend classes it might work.

      C++ is a perfectly valid school of magic.

      kshegunovK 1 Reply Last reply
      1
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #3

        Hi,

        I don't have the exact formulation at hand but you should likely be interested by std::enable_if. This blog post shows how you can use it with several conditions.

        Hope it helps

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        4
        • fcarneyF fcarney

          Why do I always find the answer after I post...

          class JMsg{
          ...
          public:
              template<class T>
              JMsg& arg(T arg){
                  m_args.append(QJsonValue(arg));
                  return *this;
              }
          ...
          };
          
          template JMsg& JMsg::arg<bool>(bool arg);
          template JMsg& JMsg::arg<int>(int arg);
          

          What I don't like is that the template function is public. I don't want another programmer to define arbitrary types to use with the class. The ones selected are the ones I want to support. I cannot define the template with a private version. I tried making it a friend template, but then it cannot access members like it did before. I am not processing the object passed to the function, so I guess that makes sense. If there were a member function version of friend classes it might work.

          kshegunovK Offline
          kshegunovK Offline
          kshegunov
          Moderators
          wrote on last edited by kshegunov
          #4

          @fcarney said in template/macro for reducing redundant functions?:

          What I don't like is that the template function is public. I don't want another programmer to define arbitrary types to use with the class. The ones selected are the ones I want to support. I cannot define the template with a private version. I tried making it a friend template, but then it cannot access members like it did before. I am not processing the object passed to the function, so I guess that makes sense. If there were a member function version of friend classes it might work.

          Then don't define the template function. Hide it's implementation in the source. That is to say - use templates as they were originally meant - as a sophisticated copy paste machinery.

          header

          class JMsg
          {
          public:
              template <typename T>
              JMsg & arg(T x);
              // ... more
          };
          
          extern template JMsg & JMsg::arg<float>(float arg);
          extern template JMsg & JMsg::arg<bool>(bool arg);
          

          source

          template <typename T>
          JMsg & JMsg::arg(T arg)
          {
              m_args.append(QJsonValue(arg));
              return *this;
          }
          
          template JMsg & JMsg::arg<bool>(bool arg);
          template JMsg & JMsg::arg<int>(int arg);

          Read and abide by the Qt Code of Conduct

          1 Reply Last reply
          4
          • fcarneyF Offline
            fcarneyF Offline
            fcarney
            wrote on last edited by
            #5

            @kshegunov said in template/macro for reducing redundant functions?:

            Hide it's implementation in the source.

            No, I mean as in can use arbitrary types. Not hiding code implementation. If the template could be made "private" as in not accessible outside the scope of the object. If the template was private and I could define a member function based upon the template then I could lock down the types with less duplication of code. However, they just don't seem to work that way.

            What I am reading is that C++ doesn't have a whole lot of ways to limit type use in templates.

            I also found I don't need this at all:

            template JMsg& JMsg::arg<bool>(bool arg);
            template JMsg& JMsg::arg<int>(int arg);
            

            For my current task I am just going to accept that I either can define the functions per type or use a template to reduce the code duplication. I can live with the too many types issue as I believe at some point the QJsonValue object will complain during compile.

            C++ is a perfectly valid school of magic.

            kshegunovK 1 Reply Last reply
            0
            • fcarneyF fcarney

              @kshegunov said in template/macro for reducing redundant functions?:

              Hide it's implementation in the source.

              No, I mean as in can use arbitrary types. Not hiding code implementation. If the template could be made "private" as in not accessible outside the scope of the object. If the template was private and I could define a member function based upon the template then I could lock down the types with less duplication of code. However, they just don't seem to work that way.

              What I am reading is that C++ doesn't have a whole lot of ways to limit type use in templates.

              I also found I don't need this at all:

              template JMsg& JMsg::arg<bool>(bool arg);
              template JMsg& JMsg::arg<int>(int arg);
              

              For my current task I am just going to accept that I either can define the functions per type or use a template to reduce the code duplication. I can live with the too many types issue as I believe at some point the QJsonValue object will complain during compile.

              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by
              #6

              I don't follow. Isn't the code I provided the thing you want?

              Read and abide by the Qt Code of Conduct

              1 Reply Last reply
              0
              • fcarneyF Offline
                fcarneyF Offline
                fcarney
                wrote on last edited by
                #7

                Okay, lets say I have this:

                class JMsg{
                ...
                public:
                    template<class T>
                    JMsg& arg(T arg){
                        m_args.append(QJsonValue(arg));
                        return *this;
                    }
                ...
                };
                

                Without anything else this allows me to do this:

                JMsg().arg(1); // an int
                JMsg().arg("hello"); // a const char*
                etc
                

                But if I don't want this:

                JMsg().arg(QJsonArray()); // which QJsonValue will accept
                

                My down stream code will probably do strange things with this. So I want the limited typing a explicitly defined member function gives, but with the copy pasta that templates provide. These are 2 opposing incompatible goals. However, if there was a way to use the template as a private to the class scope, and then define the member function using the template per type specified then it would prevent the use of problematic types. I guess if I really wanted to I could have it call a private template function, but I am wondering if it is really worth all this trouble. I guess what I really want is the functionality of a dumb macro, but with nicer syntax.

                C++ is a perfectly valid school of magic.

                kshegunovK 1 Reply Last reply
                0
                • fcarneyF fcarney

                  Okay, lets say I have this:

                  class JMsg{
                  ...
                  public:
                      template<class T>
                      JMsg& arg(T arg){
                          m_args.append(QJsonValue(arg));
                          return *this;
                      }
                  ...
                  };
                  

                  Without anything else this allows me to do this:

                  JMsg().arg(1); // an int
                  JMsg().arg("hello"); // a const char*
                  etc
                  

                  But if I don't want this:

                  JMsg().arg(QJsonArray()); // which QJsonValue will accept
                  

                  My down stream code will probably do strange things with this. So I want the limited typing a explicitly defined member function gives, but with the copy pasta that templates provide. These are 2 opposing incompatible goals. However, if there was a way to use the template as a private to the class scope, and then define the member function using the template per type specified then it would prevent the use of problematic types. I guess if I really wanted to I could have it call a private template function, but I am wondering if it is really worth all this trouble. I guess what I really want is the functionality of a dumb macro, but with nicer syntax.

                  kshegunovK Offline
                  kshegunovK Offline
                  kshegunov
                  Moderators
                  wrote on last edited by kshegunov
                  #8

                  @fcarney said in template/macro for reducing redundant functions?:

                  However, if there was a way to use the template as a private to the class scope, and then define the member function using the template per type specified then it would prevent the use of problematic types.

                  Again, this is exactly what my code does. I did not define the template in the header exactly so you can't call one of its instantiations if it was not instantiated explicitly. Look again at my snippet - you have header and source and what goes where is important.

                  Read and abide by the Qt Code of Conduct

                  1 Reply Last reply
                  2
                  • fcarneyF Offline
                    fcarneyF Offline
                    fcarney
                    wrote on last edited by fcarney
                    #9

                    @kshegunov
                    Okay, I think I get it. The template is partial if it is only provided in the header file. It needs the complete template that only exists in the cpp file. So it doesn't have the information it needs to complete the member function. This is really really sneaky. I found it will actually compile and run without these as well:

                    extern template JMsg& JMsg::arg<bool>(bool arg);
                    extern template JMsg& JMsg::arg<int>(int arg);
                    extern template JMsg& JMsg::arg<double>(double arg);
                    extern template JMsg& JMsg::arg<const QString>(const QString arg);
                    extern template JMsg& JMsg::arg<const char*>(const char* arg);
                    

                    It does throw up some semantics issues though.

                    So it pushes the problem to the linker, as in if someone does this:

                    JMsg().arg(QJsonArray());
                    

                    It should throw a linker error.

                    Thank you for taking the time to explain this to me.

                    C++ is a perfectly valid school of magic.

                    kshegunovK 1 Reply Last reply
                    0
                    • fcarneyF fcarney

                      @kshegunov
                      Okay, I think I get it. The template is partial if it is only provided in the header file. It needs the complete template that only exists in the cpp file. So it doesn't have the information it needs to complete the member function. This is really really sneaky. I found it will actually compile and run without these as well:

                      extern template JMsg& JMsg::arg<bool>(bool arg);
                      extern template JMsg& JMsg::arg<int>(int arg);
                      extern template JMsg& JMsg::arg<double>(double arg);
                      extern template JMsg& JMsg::arg<const QString>(const QString arg);
                      extern template JMsg& JMsg::arg<const char*>(const char* arg);
                      

                      It does throw up some semantics issues though.

                      So it pushes the problem to the linker, as in if someone does this:

                      JMsg().arg(QJsonArray());
                      

                      It should throw a linker error.

                      Thank you for taking the time to explain this to me.

                      kshegunovK Offline
                      kshegunovK Offline
                      kshegunov
                      Moderators
                      wrote on last edited by
                      #10

                      @fcarney said in template/macro for reducing redundant functions?:

                      Okay, I think I get it.

                      Partly. What a template is from the point of view of the binary is nothing - it does not produce anything. A template is way to tell your compiler to substitute template arguments at some point and produce a real living class (or function) which it then can compile to assembly. Instantiation happens on first use with a given set of template parameters ordinarily. In this case I "abuse" that peculiarity by saying: okay, there is a template function (the declaration) but when you need to substitute the types (i.e. instantiate the template on use) you can't, because you don't know the function body.

                      The extern template at the end is to make all that at all useful - fine there's no body, so the template can't be instantiated in the user code, but I can give you some instantiations I made explicitly somewhere else (in this case in the source). So it works like this:

                      1. The compiler sees there's a declaration. It also sees there are 2, 3, 4 instantiations that are done somewhere else (i.e. the declarations at the bottom). In the source it has the template body, and has been told to instantiate the template - that's fine, it can as it knows how.
                      2. When you use it somewhere it matches the stuff that's declared (i.e. the template declaration and the instantiations) against what you request. It puts a reference for the linker to resolve.
                      3. If there's an instantiation provided for the given set of template parameters, everything is fine - the linker's going to clean up the mess and link the function you have with the usage. If not, i.e. you used the template with a parameter that hand't been explicitly instantiated, then the linker complains - there's no such thing.

                      Read and abide by the Qt Code of Conduct

                      1 Reply Last reply
                      3

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved