Thread: Template metaprogramming - pre-procressing resource.h

  1. #1
    Registered User subdene's Avatar
    Join Date
    Jan 2002
    Posts
    367

    Template metaprogramming - pre-procressing resource.h

    Hi,

    I was just wondering if it would be theoretically possible to use Template metaprogramming to process the resource.h file, and create a map of resource string to their numberic ID's. For example:

    Code:
     
    #define ID_VIEW_APPLOOK_OFF_XP          206
    #define ID_VIEW_APPLOOK_WIN_XP          207
    #define ID_VIEW_APPLOOK_OFF_2003        208
    #define ID_VIEW_APPLOOK_VS_2005         209
    #define ID_VIEW_APPLOOK_VS_2008         210
    generating map:

    Code:
    resourceString.insert(std::pair<string, int>("ID_VIEW_APPLOOK_OFF_XP", 206))
    I guess a perl script on a pre-build step could do something similar and modify a .cpp with all the entries to build up the map.
    Be a leader and not a follower.

  2. #2
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Templates and strings do not play well. If you're willing to write out "ID_VIEW_APPLOOK_OFF_XP" as 'I','D','_','V' ect, then you could do it. But that's a real pain.

    But you could use a function (probably static inline) to shorten that long line to something short.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  3. #3
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Using a pre-build script would be reasonable, specially if your IDE makes resource.h changes in the background via GUI wizards etc...

    gg

  4. #4
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    I was just wondering if it would be theoretically possible to use Template metaprogramming to process the resource.h file, and create a map of resource string to their numberic ID's.
    The template mechanism isn't capable of expressing "IO" until run-time and even then it behaves as a kind of "monad" where it will not do what you want.

    Templates and strings do not play well.
    I grant you that many implementations don't do a good job, and that should be kept in mind, but templates and strings work perfectly well together.

    Using a pre-build script would be reasonable, specially if your IDE makes resource.h changes in the background via GUI wizards etc...
    The resource compiler, in general, isn't capable of expressing a lot of things that would make this better, but as things stand, and as muddy as a second tool is, this is still probably the way to go.

    Soma

  5. #5
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by phantomotap View Post
    I grant you that many implementations don't do a good job, and that should be kept in mind, but templates and strings work perfectly well together.
    You can pass around char pointers and std::strings with templates, but you can't read a string and compute with it using template meta-programming.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  6. #6
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    You can pass around char pointers and std::strings with templates, but you can't read a string and compute with it using template meta-programming.
    Are you talking about "IO"? I hope not.

    If you are talking about strings embedded in the source, you can read a string. The C++ standard template mechanism allows for that. It just happens that almost no compilers bother implementing it in such a way as to be useful. (You can't rely on floating point references as template parameters either.)

    I'm not sure what it would mean to "compute" a string. As for mutating a string, the C++ standard template mechanism specifically forbids some mutation in the constant face while allowing other mutations, but the real world gets in the way in any event.

    Here is a somewhat portable example of how to approach it which also serves as great explanation for why it isn't done even though it is somewhat possible.

    Soma

    Code:
    #include <cstring>
    #include <iostream>
    
    extern char gBuffer[32];
    
    template
    <
        char * FBuffer
    >
    struct STest
    {
        static char mMutate00;
        static char mMutate01;
        static char mMutate02;
        static char mMutate03;
        static char mMutate04;
        static char mMutate05;
        static char mMutate06;
        static char mMutate07;
        static char mMutate08;
        static char mMutate09;
        static char mMutate10;
        static char mMutate11;
        static char mMutate12;
        static char mMutate13;
        static char mMutate14;
        static char mMutate15;
        static char mActivate;
    };
    
    template<char * FBuffer> char STest<FBuffer>::mMutate00 = FBuffer[0] = 'I';
    template<char * FBuffer> char STest<FBuffer>::mMutate01 = FBuffer[1] = 't';
    template<char * FBuffer> char STest<FBuffer>::mMutate02 = FBuffer[2] = ' ';
    template<char * FBuffer> char STest<FBuffer>::mMutate03 = FBuffer[3] = 'c';
    template<char * FBuffer> char STest<FBuffer>::mMutate04 = FBuffer[4] = 'a';
    template<char * FBuffer> char STest<FBuffer>::mMutate05 = FBuffer[5] = 'n';
    template<char * FBuffer> char STest<FBuffer>::mMutate06 = FBuffer[6] = ' ';
    template<char * FBuffer> char STest<FBuffer>::mMutate07 = FBuffer[7] = 'b';
    template<char * FBuffer> char STest<FBuffer>::mMutate08 = FBuffer[8] = 'e';
    template<char * FBuffer> char STest<FBuffer>::mMutate09 = FBuffer[9] = ' ';
    template<char * FBuffer> char STest<FBuffer>::mMutate10 = FBuffer[10] = 'd';
    template<char * FBuffer> char STest<FBuffer>::mMutate11 = FBuffer[11] = 'o';
    template<char * FBuffer> char STest<FBuffer>::mMutate12 = FBuffer[12] = 'n';
    template<char * FBuffer> char STest<FBuffer>::mMutate13 = FBuffer[13] = 'e';
    template<char * FBuffer> char STest<FBuffer>::mMutate14 = FBuffer[14] = '!';
    template<char * FBuffer> char STest<FBuffer>::mMutate15 = FBuffer[15] = '\0';
    
    template<char * FBuffer> char STest<FBuffer>::mActivate = STest<FBuffer>::mMutate00 + STest<FBuffer>::mMutate01 + STest<FBuffer>::mMutate02 + STest<FBuffer>::mMutate03 + STest<FBuffer>::mMutate04 + STest<FBuffer>::mMutate05 + STest<FBuffer>::mMutate06 + STest<FBuffer>::mMutate07 + STest<FBuffer>::mMutate08 + STest<FBuffer>::mMutate09 + STest<FBuffer>::mMutate10 + STest<FBuffer>::mMutate11 + STest<FBuffer>::mMutate12 + STest<FBuffer>::mMutate13 + STest<FBuffer>::mMutate14 + STest<FBuffer>::mMutate15;
    
    char gBuffer[32] = "Can this be done?";
    
    int main()
    {
        using namespace std;
    #   if !defined(EXPAND)
            STest<gBuffer>::mActivate;
    #   endif
        if(strlen(gBuffer) > 0)
        {
            cout << gBuffer << '\n';
        }
        return(0);
    }

  7. #7
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by phantomotap View Post
    Are you talking about "IO"? I hope not.

    If you are talking about strings embedded in the source, you can read a string. The C++ standard template mechanism allows for that. It just happens that almost no compilers bother implementing it in such a way as to be useful. (You can't rely on floating point references as template parameters either.)

    I'm not sure what it would mean to "compute" a string. As for mutating a string, the C++ standard template mechanism specifically forbids some mutation in the constant face while allowing other mutations, but the real world gets in the way in any event.

    Here is a somewhat portable example of how to approach it which also serves as great explanation for why it isn't done even though it is somewhat possible.

    Soma

    Code:
    #include <cstring>
    #include <iostream>
    
    extern char gBuffer[32];
    
    template
    <
        char * FBuffer
    >
    struct STest
    {
        static char mMutate00;
        static char mMutate01;
        static char mMutate02;
        static char mMutate03;
        static char mMutate04;
        static char mMutate05;
        static char mMutate06;
        static char mMutate07;
        static char mMutate08;
        static char mMutate09;
        static char mMutate10;
        static char mMutate11;
        static char mMutate12;
        static char mMutate13;
        static char mMutate14;
        static char mMutate15;
        static char mActivate;
    };
    
    template<char * FBuffer> char STest<FBuffer>::mMutate00 = FBuffer[0] = 'I';
    template<char * FBuffer> char STest<FBuffer>::mMutate01 = FBuffer[1] = 't';
    template<char * FBuffer> char STest<FBuffer>::mMutate02 = FBuffer[2] = ' ';
    template<char * FBuffer> char STest<FBuffer>::mMutate03 = FBuffer[3] = 'c';
    template<char * FBuffer> char STest<FBuffer>::mMutate04 = FBuffer[4] = 'a';
    template<char * FBuffer> char STest<FBuffer>::mMutate05 = FBuffer[5] = 'n';
    template<char * FBuffer> char STest<FBuffer>::mMutate06 = FBuffer[6] = ' ';
    template<char * FBuffer> char STest<FBuffer>::mMutate07 = FBuffer[7] = 'b';
    template<char * FBuffer> char STest<FBuffer>::mMutate08 = FBuffer[8] = 'e';
    template<char * FBuffer> char STest<FBuffer>::mMutate09 = FBuffer[9] = ' ';
    template<char * FBuffer> char STest<FBuffer>::mMutate10 = FBuffer[10] = 'd';
    template<char * FBuffer> char STest<FBuffer>::mMutate11 = FBuffer[11] = 'o';
    template<char * FBuffer> char STest<FBuffer>::mMutate12 = FBuffer[12] = 'n';
    template<char * FBuffer> char STest<FBuffer>::mMutate13 = FBuffer[13] = 'e';
    template<char * FBuffer> char STest<FBuffer>::mMutate14 = FBuffer[14] = '!';
    template<char * FBuffer> char STest<FBuffer>::mMutate15 = FBuffer[15] = '\0';
    
    template<char * FBuffer> char STest<FBuffer>::mActivate = STest<FBuffer>::mMutate00 + STest<FBuffer>::mMutate01 + STest<FBuffer>::mMutate02 + STest<FBuffer>::mMutate03 + STest<FBuffer>::mMutate04 + STest<FBuffer>::mMutate05 + STest<FBuffer>::mMutate06 + STest<FBuffer>::mMutate07 + STest<FBuffer>::mMutate08 + STest<FBuffer>::mMutate09 + STest<FBuffer>::mMutate10 + STest<FBuffer>::mMutate11 + STest<FBuffer>::mMutate12 + STest<FBuffer>::mMutate13 + STest<FBuffer>::mMutate14 + STest<FBuffer>::mMutate15;
    
    char gBuffer[32] = "Can this be done?";
    
    int main()
    {
        using namespace std;
    #   if !defined(EXPAND)
            STest<gBuffer>::mActivate;
    #   endif
        if(strlen(gBuffer) > 0)
        {
            cout << gBuffer << '\n';
        }
        return(0);
    }
    That example perfectly shows how they don't play well together. Having to write out "it can be done!" character by character isn't playing well.

    A more general use for such a thing would be hardcoding a password hash. It would be really nice to be able to type md6<"default password">() and produce a binary that does not contain the "default password" string. That's possible, but only if you type it like this: md6<'d','e','f','a','u','l','t'>. Which is quite inconvenient.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    That example perfectly shows how they don't play well together.
    O_o

    There seems to be some miscommunication.

    "templates" != "template meta-programming"

    My response to "Templates and strings do not play well." was that they do play perfectly well together where the example that I thought brought this into the discussion was `resourceString.insert(std:air<string, int>("ID_VIEW_APPLOOK_OFF_XP", 206))' which is perfectly acceptable.

    Then you said "you can't read a string and compute with it using template meta-programming" where my response was that such is something that can be done in the context of strings embedded in the source.

    The original post brought up meta-programming in the context of processing a text file at compile-time which can't be done because the template mechanism can't do "IO" at compile-time.

    On reading your first post again I now see what you were saying there, but as far as that goes, you are simply wrong in the implication and may have missed the goal of the question as well. Being able to write `atemplate<"somedata">' doesn't help because writing `atemplate<'s','o','m','e','d','a','t','a'>' doesn't allow you to be able to read and process a file ("resource.h") at compile-time to get "somedata".

    Having to write out "it can be done!" character by character isn't playing well.
    Template meta-programming is by its nature verbose and ugly being as the entire concept is an accident of birth not an intended part of the language.

    It would be really nice to be able to type md6<"default password">() and produce a binary that does not contain the "default password" string.
    You are right. That isn't possible. It would have to look like the following example.

    Code:
    // ...
    char gDefault="default password";
    // ...
    md6<gDefault>::hash;
    // ...
    It just doesn't get you anywhere because many compilers don't allow for it, and even those that do don't necessarily make it reasonable.

    Soma

  9. #9
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    You could do this with macro trickery. For example,
    Code:
    DO(MODE_NORMAL)
    DO(MODE_ONE)
    DO(MODE_TWO)
    // ...
    Then include the header either with DO defined such that an enum will be generated, or with DO defined so that string names will be generated. Perhaps both. Nothing preventing a macro from being redefined. For example:
    Code:
    #define DO(name) \
        #name,
    
    const char *names[] = {
        #include "dolist.hpp"
    };
    #undef DO
    Generating a map out of that would be easy, or you could use the strings/enums directly. You could do something similar if you wanted control over exactly which IDs were assigned to which values (pass an extra parameter to DO...).

    Personally I'd probably just write a Perl script and include it in the build system. Much simpler and easier to maintain. The script would probably be really fast, so you could run it at every build and it would auto-generate some headers. Make the script leave headers alone unless it determines it would generate something different from what was already on disk. (Just compute the hash of the existing file, and the hash of the data you want to generate.) That would prevent the compiler from re-compiling the same stuff over and over again.

    Just some food for thought.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  10. #10
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by phantomotap View Post
    Template meta-programming is by its nature verbose and ugly being as the entire concept is an accident of birth not an intended part of the language.
    Yes, but usually it manages to make the interface decent.

    You are right. That isn't possible. It would have to look like the following example.

    Code:
    // ...
    char gDefault="default password";
    // ...
    md6<gDefault>::hash;
    // ...
    It just doesn't get you anywhere because many compilers don't allow for it, and even those that do don't necessarily make it reasonable.

    Soma
    No, most compilers don't let you assign a string literal to a char. It is possible to pass a char * or char [] as a template parameter, but it is not possible to access the values pointed to compile time. Arrays in template function parameters decay to pointers just like regular function parameters.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  11. #11
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Yes, but usually it manages to make the interface decent.
    That is matter of opinion.

    No, most compilers don't let you assign a string literal to a char.
    You are right; there is a bug in my code. I left out the "array operator".

    Code:
    char gDefault[]="default password";
    It is possible to pass a char * or char [] as a template parameter, but it is not possible to access the values pointed to compile time.
    As I said, "It just doesn't get you anywhere because many compilers don't allow for it, and even those that do don't necessarily make it reasonable." which isn't the same thing at all as "not possible". It is possible. It will continue to be possible. You are wrong. You can say that a million times; I promise, you'll continue to be wrong.

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 9
    Last Post: 04-06-2011, 01:37 PM
  2. template metaprogramming problem
    By TriKri in forum C++ Programming
    Replies: 5
    Last Post: 10-29-2009, 11:05 AM
  3. Template metaprogramming, whats the point?
    By Cogman in forum C++ Programming
    Replies: 26
    Last Post: 02-01-2009, 11:47 PM
  4. Metaprogramming Issue
    By schultz in forum C++ Programming
    Replies: 8
    Last Post: 10-08-2007, 05:11 PM
  5. template metaprogramming
    By Mitsukai in forum C++ Programming
    Replies: 8
    Last Post: 01-20-2006, 05:56 AM