Desenvolvimento - C/C++

Usando Win32 API para otimizar o I/O, parte 2

Como eu já disse em um artigo anterior, estou fazendo um parser de arquivos IDL, usando somente C++ ISO (nada de Win32), STL e Boost. Apesar de estar realmente impressionado com o desempenho do meu parser do jeito que está, eu me propus a testar o nível de otimização de I/O que seria possível usando a API Win32 diretamente. Esta é a segunda parte do artigo.

por Rodrigo Strauss



Antes de começar a otimização do I/O do parser, é necessário fazer algumas considerações e algumas medições. A proposta inicial é otimizar o I/O do parser, mas será que vale mesmo a pena?

Fiz algumas medições para saber quanto tempo o parser demora para ler os arquivos que ele interpreta. Depois de várias medições (aproximadamente 50), cheguei a conclusão de que gastamos, em média, 20% do tempo com I/O. Em situações onde o computador acabou de ser ligado - ou seja, quando os arquivos não estão no Cache Manager do Windows - o tempo de acesso aos arquivos pode chegar a 50% do tempo gasto pelo parser. Não esqueça que só estamos medindo o tempo necessário para ler os arquivos (que como eu já disse, somam aproximadamente 400kb), não gravamos nada.

Tempo médio gasto com I/O:  20%
Tempo máximo gasto com I/O: 50%

Já que a interpretação do arquivo em si leva 80% do tempo em média, não valeria mais a pena otimizá-la e de deixar o I/O para depois? Minha resposta é não, e vou explicar os motivos. A interpretação é o core do sistema, e o código que lida com isso foi feito da forma mais clara possível, para facilitar a manutenção e melhorias. E eu não estou disposto a sacrificar a clareza do código para que o parser fique mais rápido. Sendo assim, a otimização seria mais difícil, porque provavelmente envolveria mudanças em algoritmos, além de ser feita com essas requisição (clareza do código) em mente.

O motivo principal para otimizar o I/O é que, de acordo com o que vimos no post anterior, o I/O corresponde a menos de 4 linhas de código, enquando a classe de parser tem aproximadamente 850 linhas. Essas 4 linhas de código, algumas vezes, chegam a representar 50% do tempo gasto. Sendo assim, o custo-benefício da alteração me pareceu muito satisfatório, já que é fácil otimizar o I/O, afinal, ele é uma parte pontual e fácil de ser isolada e compreendida. A otimização do I/O não vai comprometer a legibilidade do código.

Conclusão: vale a pena otimizar o I/O. Depois dessas medições, começaremos a testar as otimizações possíveis e comprar os resultados.

Fontes do MidlParser

A função que dá início à interpretação do arquivo é a MidlParser::ParseMidlFile

CodeEntities.h
#pragma once

struct IdlAttribute
{
   IdlAttribute(const std::string& name, const std::string& value) : Name(name), Value(value) {}
   IdlAttribute(const std::string& name) : Name(name){}

   std::string Name;
   std::string Value;
};

struct IdlParameter
{
   IdlParameter(const std::string& type, const std::string& name) : Type(type), Name(name), IndirectionLevel(0) {}
   IdlParameter() : IndirectionLevel(0) {}

   typedef std::vector<IdlAttribute> AttributesContainer;
   typedef AttributesContainer::iterator AttributesIterator;
   typedef AttributesContainer::const_iterator ConstAttributesIterator;

   AttributesContainer Attributes;
   
   std::string Type;
   std::string Name;
   unsigned int IndirectionLevel;
};

struct IdlMethod
{
   IdlMethod(const std::string& name, const std::string& returnType) : Name(name), ReturnType(returnType) {}
   IdlMethod() {}

   typedef std::vector<IdlAttribute> AttributesContainer;
   typedef AttributesContainer::iterator AttributesIterator;
   typedef AttributesContainer::const_iterator ConstAttributesIterator;

   typedef std::vector<IdlParameter> ParametersContainer;
   typedef ParametersContainer::iterator ParametersIterator;
   typedef ParametersContainer::const_iterator ConstParametersIterator;


   AttributesContainer Attributes;
   std::string ReturnType;
   std::string Name;
   ParametersContainer Parameters;
};

struct IdlInterface
{
   IdlInterface(const std::string& name) : Name(name) {}
   IdlInterface() {}

   typedef std::vector<IdlAttribute> AttributesContainer;
   typedef AttributesContainer::iterator AttributesIterator;
   typedef AttributesContainer::const_iterator ConstAttributesIterator;

   typedef std::vector<IdlMethod> MethodsContainer;
   typedef MethodsContainer::iterator MethodsIterator;
   typedef MethodsContainer::const_iterator ConstMethodsIterator;

   AttributesContainer Attributes;
   std::string Name;
   std::string InheritsFrom;
   MethodsContainer Methods;
};

//
// functor
//
class IdlAttributeFindByName
{
private:
   std::string m_strName;
   //
   // Deve ser um shared_ptr pq um functor STL é passado como valor, e não como referência.
   // Se eu usar auto_ptr, na primeira cópia o ownership do ponteiro vao para a cópia e
   // ficamos sem nada...
   //
   boost::shared_ptr<std::vector<std::string> > m_MoreAttributesToFind;
public:
   IdlAttributeFindByName(const std::string& name)
      : m_strName(name)
   {}

   IdlAttributeFindByName()
   {}

   void AddAttributeToFind(const std::string& str)
   {
      if(m_MoreAttributesToFind.get() == NULL)
         m_MoreAttributesToFind = boost::shared_ptr<std::vector<std::string> >(new std::vector<std::string>());

      m_MoreAttributesToFind->push_back(str);
   }

   bool operator()(const IdlAttribute& att)
   {
      if(m_MoreAttributesToFind.get() == NULL)
         return att.Name == m_strName;
      else
      {
         return att.Name == m_strName || 
            std::find(m_MoreAttributesToFind->begin(), m_MoreAttributesToFind->end(), att.Name) != 
m_MoreAttributesToFind->end();
      }

   }
};

MidlParser.h
#pragma once

//
// MidlParser
// 
// Faz o parser de um arquivo IDL
//
class MidlParser
{
public:

   class ParseException : public std::exception
   {
   private:
      unsigned int m_line;
      string m_fileName;
   public:
      ParseException(const string& what, MidlParser& parser, const string& fileName) 
         : std::exception(what.c_str()), 
           m_line(parser.GetCurrentLine()),
           m_fileName(fileName)
      {
      }

      void set_line(unsigned int line)
      {
         m_line = line;
      }

      unsigned int get_line() const
      {
         return m_line;
      }

      void set_fileName(const string& fileName)
      {
         m_fileName = fileName;
      }

      const string& get_fileName() const
      {
         return m_fileName;
      }
   };

private:

   bool m_bParseAsImportFile;

   string m_parsedFileName;

   typedef tokenizer<char_separator<char> > TokenizerMidl;
   vector<string> m_KnownInterfaces;
   vector<string> m_KnownTypes;
   map<string, IdlInterface> m_ParsedInterfaces;

   vector<string> m_IncludePaths;

   vector<string> m_ParsedIncludeFiles;

   TokenizerMidl m_Tokenizer;
   TokenizerMidl::iterator m_TokenIterator;
   std::string m_strInvalidToken;

   TokenizerMidl::iterator m_LineCountIterator;
   mutable unsigned int m_uiCurrentLine;

#ifdef _DEBUG
   //
   // isso facilita a visualização em debug
   //
   std::string m_strCurrentToken;
   const char* m_szCurrentToken;
   unsigned int m_uiBreakOnLine;
#endif

   void Log(const string& msg);

   //
   // verifica se é um identificador válido
   //
   const string& Identifier(const string& token);
   const string& NextTokenAsIdentifier();
   const string& CurrentTokenAsIdentifier();

   void CheckIsExpectedToken(const string& str, const string& checked, const char* expected_name = NULL);
   void CheckExpectedNextToken(const std::string expected, const char* expected_name = NULL);
   void CheckExpectedCurrentToken(const std::string expected, const char* expected_name = NULL);
   void CheckNotExpectedToken(const string& str, const string& checked, const char* not_expected_name = NULL);
   void CheckNotExpectedNextToken(const std::string not_expected, const char* not_expected_name = NULL);
   void CheckNotExpectedCurrentToken(const std::string not_expected, const char* not_expected_name = NULL);
   
   void CheckType(const string& str, unsigned int indirectionLevel, bool mustBeInterface = false);
   
   const string& NextTokenAsType(unsigned int indirectionLevel = 0, bool mustBeInterface = false);   
   const string& CurrentTokenAsType(unsigned int indirectionLevel = 0, bool mustBeInterface = false);

   void AddParsedInterface(const IdlInterface& iface);
   void AddKnownInterfaceName(const string& interfaceName);


   unsigned int GetCurrentLine();


   const std::string& NextToken(bool checkEndOfFile = true, bool skipComments = true);
   const std::string& CurrentToken(bool checkEndOfFile = true, bool skipComments = true);

   void SkipUntilToken(const string& token);
   void SkipQuote();
   void SkipUntilClose(const string& open_token, const string& close_token);

   void LoadKnownInterfaces();
   void LoadKnownTypes();

   void ParseMethod(IdlMethod* method);
   void ParseInterface(IdlInterface* iface);
   void ParseAttributes(vector<IdlAttribute>* attributes);
   void ParseImportFile(const string& fileName);

   bool IsEndOfFile();

   bool OpenIncludeFile(const string& fileName, fstream* f, string* fileNameWithPath);
   

public:

   MidlParser();
   void AddFindPath(const string& path);
   void ParseMidlFile(const char* fileName);

   const std::map<string, IdlInterface>& GetParsedInterfaces() const;
   const std::vector<string>& GetKnownInterfaces() const;

   void Dump(ostream& s) const;
   
private:
   void AddKnownType(const string& t);
};


MidlParser.cpp
#pragma once
#include "stdafx.h"

#include "CodeEntities.h"
#include "MidlParser.h"


//
// verifica se é um identificador válido
//
const string& MidlParser::Identifier(const string& token)
{
   bool isValid = true;

   if(token.size() == 0 || (!std::isalpha(token[0]) && token[0] != "_"))
      isValid = false;
   else
   {
      for(string::const_iterator i = token.begin() ; i != token.end() ; ++i)
      {
         char c = *i;
         if(!std::isalpha(c) && !std::isalnum(c) && c != "_")
         {
            isValid = false;
            break;
         }
      }
   }

   if(!isValid)
      throw ParseException("invalid identifier: "" + token + """, *this, m_parsedFileName);

   return token;
}

const string& MidlParser::NextTokenAsIdentifier()
{
   return Identifier(NextToken());
}

const string& MidlParser::CurrentTokenAsIdentifier()
{
   return Identifier(CurrentToken());
}

void MidlParser::CheckIsExpectedToken(const string& str, const string& checked, const char* expected_name)
{
   if(str != checked)
   {
      string ex("expected: ");

      if(expected_name)
         ex += expected_name;
      else
      {
         ex += """;
         ex += checked;
         ex += """;
      }

      throw ParseException(ex, *this, m_parsedFileName);
   }
}

void MidlParser::CheckExpectedNextToken(const std::string expected, const char* expected_name)
{
   CheckIsExpectedToken(NextToken(), expected, expected_name);
}

void MidlParser::CheckExpectedCurrentToken(const std::string expected, const char* expected_name)
{
   CheckIsExpectedToken(CurrentToken(), expected, expected_name);
}

void MidlParser::CheckNotExpectedToken(const string& str, const string& checked, const char* not_expected_name)
{
   if(str == checked)
   {
      string ex("not expected: ");

      if(not_expected_name)
         ex += not_expected_name;
      else
      {
         ex += """;
         ex += checked;
         ex += """;
      }

      throw ParseException(ex, *this, m_parsedFileName);
   }
}

void MidlParser::CheckNotExpectedNextToken(const std::string not_expected, const char* not_expected_name)
{
   CheckNotExpectedToken(NextToken(), not_expected, not_expected_name);
}

void MidlParser::CheckNotExpectedCurrentToken(const std::string not_expected, const char* not_expected_name )
{
   CheckNotExpectedToken(CurrentToken(), not_expected, not_expected_name);
}


void MidlParser::CheckType(const string& str, unsigned int indirectionLevel, bool mustBeInterface )
{

   if(std::find(m_KnownInterfaces.begin(), m_KnownInterfaces.end(), str) == m_KnownInterfaces.end())
   {
      if(mustBeInterface == true)
         throw ParseException("Unknwon Type: "" + str + """, *this, m_parsedFileName);
   }
   else
      return;


   if(std::find(m_KnownTypes.begin(), m_KnownTypes.end(), str) == m_KnownTypes.end())
      throw ParseException("Unknwon Type: "" + str + """, *this, m_parsedFileName);
}

const string& MidlParser::NextTokenAsType(unsigned int indirectionLevel, bool mustBeInterface)
{
   CheckType(NextToken(), indirectionLevel, mustBeInterface);
   return CurrentToken();
}

const string& MidlParser::CurrentTokenAsType(unsigned int indirectionLevel, bool mustBeInterface)
{
   CheckType(CurrentToken(), indirectionLevel, mustBeInterface);
   return CurrentToken();
}

void MidlParser::AddKnownInterfaceName(const string& interfaceName)
{
   if(std::find(m_KnownInterfaces.begin(), m_KnownInterfaces.end(), interfaceName) == m_KnownInterfaces.end())
      m_KnownInterfaces.push_back(interfaceName);
}


void MidlParser::AddParsedInterface(const IdlInterface& iface)
{
   m_ParsedInterfaces.insert(make_pair<string, IdlInterface>(iface.Name, iface));
   m_KnownInterfaces.push_back(iface.Name); 
   AddKnownType(iface.Name);
}



unsigned int MidlParser::GetCurrentLine()
{
   for( ; m_LineCountIterator != m_TokenIterator ; ++m_LineCountIterator)
      if(*m_LineCountIterator == "n")
         m_uiCurrentLine++;

   return m_uiCurrentLine;
}


const std::string& MidlParser::NextToken(bool checkEndOfFile, bool skipComments)
{
   ++m_TokenIterator;

   return CurrentToken(checkEndOfFile, skipComments);
}

const std::string& MidlParser::CurrentToken(bool checkEndOfFile, bool skipComments)
{
   if(IsEndOfFile())
   {
      if(checkEndOfFile)
         throw ParseException("unexpected end of file", *this, m_parsedFileName);
      else
         return m_strInvalidToken;
   }

   if(skipComments)
   {
      for(;;)
      {
         if(*m_TokenIterator != "/" && *m_TokenIterator != "n")
            break;
         //
         // pula os comentários
         //
         while(*m_TokenIterator == "/")
         {
            TokenizerMidl::iterator next = m_TokenIterator;
            next++;

            if(*next == "/")
            {
               while(m_TokenIterator != m_Tokenizer.end() && *++m_TokenIterator != "n");
               
               break;
            }
            else if(*next == "*")
            {
               ++m_TokenIterator;

               for(;;)
               {
                  ++m_TokenIterator;

                  if(IsEndOfFile())
                  {
                     if(checkEndOfFile)
                        throw ParseException("unexpected end of file", *this, m_parsedFileName);
                     else
                        return m_strInvalidToken;
                  }

                  const string& token = *m_TokenIterator;

                  if(token == "*")
                  {
                     ++m_TokenIterator;

                     if(IsEndOfFile())
                     {
                        if(checkEndOfFile)
                           throw ParseException("unexpected end of file", *this, m_parsedFileName);
                        else
                           return m_strInvalidToken;
                     }

                     if(*m_TokenIterator == "/")
                     {
                        ++m_TokenIterator;
                        break;
                     }
                  }
               }
            }
            else
               break;
         }

         //
         // pula os n
         //
         while(++m_TokenIterator != m_Tokenizer.end() && *m_TokenIterator == "n");

         if(IsEndOfFile())
         {
            if(checkEndOfFile)
               throw ParseException("unexpected end of file", *this, m_parsedFileName);
            else
               return m_strInvalidToken;
         }
      }
   }



#ifdef _DEBUG
   //
   // isso facilita a visualização em debug
   //
   m_strCurrentToken = *m_TokenIterator;
   m_szCurrentToken = m_strCurrentToken.c_str();

   if(GetCurrentLine() >= m_uiBreakOnLine)
   {
      m_uiBreakOnLine = 0xFFFFFFFF;
      __asm int 3;
   }

#endif

   return *m_TokenIterator;
}

void MidlParser::SkipUntilToken(const string& token)
{
   std::find(m_TokenIterator, m_Tokenizer.end(), token);
}

void MidlParser::SkipQuote()
{
   bool stringOpened = false;
   for(;;)
   {
      const string& token = NextToken(true, false);
      
      if(token == """)
      {
         if(stringOpened)
            return;
         else
         {
            stringOpened = true;
            continue;
         }
      }

      //
      // pular escape de aspa
      //
      if(token == "" && NextToken(true, false) == """)
         NextToken(true, false);
   }
}

void MidlParser::SkipUntilClose(const string& open_token, const string& close_token)
{
   unsigned int deep = 0;

   for(;;)
   {
      if(CurrentToken() == open_token)
      {
         deep++;
      }
      else if(CurrentToken() == close_token)
      {
         deep--;

         if(deep == 0)
            break;
      }

      NextToken();
   }
}

void MidlParser::ParseMethod(IdlMethod* method)
{
   if(CurrentToken() == "[")
      ParseAttributes(&method->Attributes);

   method->ReturnType = CurrentTokenAsIdentifier();
   method->Name = NextTokenAsIdentifier();

   CheckExpectedNextToken("(");

   if(NextToken() != ")")
   {
      for(unsigned int uiCurrentParameter = 0 ; ; ++uiCurrentParameter)
      {
         IdlParameter param;

         //
         // vamos ver se é atributos
         //
         if(CurrentToken() == "[")
         {
            ParseAttributes(&param.Attributes);
         }

         param.Type = CurrentTokenAsIdentifier();
         
         while(NextToken() == "*")
            param.IndirectionLevel++;

         //
         // tratar sintaxe Metodo(void);
         //
         if(param.Type == "void" && CurrentToken() == ")")
         {
            break;
         }

         CheckType(param.Type, param.IndirectionLevel);

         param.Name = CurrentTokenAsIdentifier();

         method->Parameters.push_back(param);

         if(NextToken() == ")")
         {
            break;
         }

         if(CurrentToken() == ",")
         {
            NextToken();
            continue;
         }

         throw ParseException("expected: "," or ")"", *this, m_parsedFileName);
      }
   }

   NextToken();
   CheckExpectedCurrentToken(";");
   NextToken();
}

void MidlParser::ParseInterface(IdlInterface* iface)
{
   assert(CurrentToken() == "interface");

   iface->Name = NextTokenAsIdentifier();

   //
   // herança de interface
   //
   if(NextToken() == ":")
   {
      CheckNotExpectedNextToken("{", "interface name");

      iface->InheritsFrom = CurrentTokenAsType();

      CheckType(iface->InheritsFrom, 0, true);

      NextToken();
   }

   CheckExpectedCurrentToken("{");

   NextToken();

   //
   // temos que colocar a própria interface na lista de tipos
   // conhecidos, já que um método pode receber um ponteiro
   // do próprio tipo
   //
   AddKnownType(iface->Name);

   

   //
   // métodos
   //
   if(CurrentToken() != "}")
   {
      for(;;)
      {
         IdlMethod method;

         ParseMethod(&method);

         iface->Methods.push_back(method);

         //
         // fim da interface
         //
         if(CurrentToken() == "}")
            break;
      }
   }

   CheckExpectedNextToken(";");
   NextToken();
}

void MidlParser::LoadKnownInterfaces()
{
   //
   // essa maravilha de sintaxe é graças ao boost::assign
   //
   m_KnownInterfaces.clear();
   m_KnownInterfaces += "IUnknown", "IDispatch";
   return;
}

void MidlParser::LoadKnownTypes()
{
   //
   // por enquanto vou colocar os defines tb, para facilitar. Quando
   // isso tiver um pré-processador isso deve ser tirado
   //
   m_KnownTypes.clear();
   m_KnownTypes += "BSTR", "VARIANT", "DWORD", "LONG", "ULONG", "GUID", "REFGUID", 
                   "HRESULT", "BOOL", "LPVOID", "REFCLSID", "WCHAR", "REFIID", "VOID", "void", 
                   "VARIANT_BOOL", "USHORT", "int", "short", "UCHAR";

}

void MidlParser::ParseAttributes(vector<IdlAttribute>* attributes)
{
   std::stringstream buffer;

   assert(CurrentToken() == "[");

   for(;;)
   {
      if(CurrentToken() == "]")
         break;

      IdlAttribute att(NextTokenAsIdentifier());

      //
      // se for vírgula, acabou esse
      //
      if(NextToken() == ",")
      {
         attributes->push_back(att);
         continue;
      }

      //
      // se abre parenteses, tem valor
      //
      if(CurrentToken() == "(")
      {
         if(NextToken() == ")")
            throw ParseException((boost::format("Attribute %1% has null 
value") % CurrentToken()).str(), *this, m_parsedFileName);

         //
         // vamos encontrar o fim do parênteses
         //
         buffer.clear();

         while(CurrentToken() != ")")
         {
            buffer << CurrentToken();

            NextToken();
         }

         NextToken();

         att.Value = buffer.str();
      }

      attributes->push_back(att);
   }

   NextToken();
}

void MidlParser::Log(const string& msg)
{
   cerr << msg << endl;
}


bool MidlParser::IsEndOfFile()
{
   return m_TokenIterator == m_Tokenizer.end();
}

bool MidlParser::OpenIncludeFile(const string& fileName, fstream* f, string* fileNameWithPath)
{
   for(vector<string>::const_iterator i = m_IncludePaths.begin() ; i != m_IncludePaths.end() ; ++i)
   {
      string str;
      
      f->close();
      f->clear();

      str = *i + fileName;

      f->open(str.c_str(), ios::in);
      
      if(f->good())
      {
         if(fileNameWithPath)
            *fileNameWithPath = str;

         return true;
      }
   }
   return true;
}


void MidlParser::ParseImportFile(const string& fileName)
{
   fstream f;
   string str;

   //
   // deve ter extensão IDL e não ter sido interpretado ainda
   //
   if(fileName.length() < 5 || 
      fileName.substr(fileName.size() - 4, 4) != ".idl" ||
      std::find(m_ParsedIncludeFiles.begin(), m_ParsedIncludeFiles.end(), fileName) != m_ParsedIncludeFiles.end())
      return;

   MidlParser ImportParser;
   
   ImportParser.m_bParseAsImportFile = true;

   ImportParser.m_KnownTypes = m_KnownTypes;
   ImportParser.m_KnownInterfaces = m_KnownInterfaces;
   ImportParser.m_ParsedIncludeFiles = m_ParsedIncludeFiles;


   ImportParser.ParseMidlFile(fileName.c_str());

   m_KnownTypes = ImportParser.m_KnownTypes;
   m_KnownInterfaces = ImportParser.m_KnownInterfaces;
   m_ParsedIncludeFiles = ImportParser.m_ParsedIncludeFiles;
}



//
// 
// PUBLIC
// 
//

MidlParser::MidlParser() : 
   m_Tokenizer(std::string()),
   m_bParseAsImportFile(false)
{
#ifdef _DEBUG
   m_uiBreakOnLine = 0xFFFFFFFF;
#endif

   AddFindPath("C:Program FilesMicrosoft SDKinclude");
}


void MidlParser::ParseMidlFile(const char* fileName)
{
   fstream f;
   string str;
   std::vector<IdlAttribute> attributes;
 
   if(m_bParseAsImportFile)
   {
      if(!OpenIncludeFile(fileName, &f, &m_parsedFileName))
         throw ParseException(string("error opening import file "") + fileName + """, *this, m_parsedFileName);

      getline(f, str, f.widen(EOF));

      m_Tokenizer.assign(str,char_separator<char>("\t\r " , "\n\"*,;:{}/[]()"));

      m_ParsedIncludeFiles.push_back(fileName);

      Log(string("Parsing import file ") + fileName);

   }
   else
   {
      f.open(fileName, ios::in);

      if(f.fail())
         throw ParseException(string("error opening file "") + fileName + """, *this, m_parsedFileName);
   
      getline(f, str, f.widen(EOF));
      m_Tokenizer.assign(str,char_separator<char>("\t\r " , "\n\"*,;:{}/[]()"));

      m_parsedFileName = fileName;

      LoadKnownInterfaces();
      LoadKnownTypes();

   }

   
   m_TokenIterator = m_Tokenizer.begin();
   m_LineCountIterator = m_TokenIterator;
   m_uiCurrentLine = 1;

   for(;;)
   {
      const string token = CurrentToken(false);

      if(IsEndOfFile())
         break;

      //
      // início de atributos
      //
      if(token == "[")
      {
         if(m_bParseAsImportFile)
         {
            SkipUntilClose("[", "]");
            continue;
         }

         attributes.clear();

         ParseAttributes(&attributes);

         //
         // se não for atributo de uma interface vamos ignorar. 
         // Acho melhor do que verificar antes se é uma interface e ter que "rebobinar"
         // o iterator (o que, na realidade, não é possível pq o iterator é forward-only)
         //
         if(CurrentToken() != "interface")
            attributes.clear();
      }
      else if(token == "cpp_quote")
      {
         SkipQuote();
         CheckExpectedNextToken(")");
      }
      else if(token == "coclass")
      {
         SkipUntilClose("{", "}");
         NextToken();
      }
      else if(token == "import")
      {
         stringstream stm;
         
         CheckExpectedNextToken(""");
         
         while(NextToken(true, false) != """)
            stm << CurrentToken(true, false);

         ParseImportFile(stm.str());
         
         CheckExpectedNextToken(";");
         NextToken();
      }
      else if(token == "#define")
      {
         for(;;)
         {
            if(NextToken(true, false) == "n")
               break;

            //
            // se for  vamos pular um token, pq se o próximo
            // for um n ele será pulado. Senão não tem efeito mesmo...
            //
            if(CurrentToken(true, false) == "")
               NextToken(true, false);
         }

      }
      else if(token == "interface")
      {
         if(m_bParseAsImportFile)
         {
            AddKnownInterfaceName(NextTokenAsIdentifier());
            
            if(NextToken() != ";")
               SkipUntilClose("{", "}");

            continue;
         }

         IdlInterface iface;

         if(attributes.empty())
         {
            //
            // se não tem atributos TEM QUE SER um forward declaration
            //
            string name;

            name = NextTokenAsIdentifier();

            CheckExpectedNextToken(";");

            NextToken();

            m_KnownInterfaces.push_back(name);

            continue;
         }

         //
         // coloca os atributos encontrados na interface
         //
         iface.Attributes = attributes;
         attributes.clear();

         ParseInterface(&iface);

         AddParsedInterface(iface);
      }
      else if(token == "enum")
      {
         //
         // por enquanto só vamos pegar o nome do enum para considerarmos
         // como um tipo válido
         //
         SkipUntilClose("{", "}");
         
         if(NextToken() != ";")
         {
            AddKnownType(CurrentTokenAsIdentifier());
            CheckExpectedNextToken(";");
         }
         NextToken();
      }
      else
      {
         NextToken(false);
      }
   }

   if(!m_bParseAsImportFile)
   {
      cout << "Finish (" << g_dwIoTime << " / " << dwTickCount << " / " << std::fixed << percent << ")" << endl;
   }

   return;
}


void MidlParser::AddFindPath(const string& path)
{
   if(std::find(m_IncludePaths.begin(), m_IncludePaths.end(), path) == m_IncludePaths.end())
   {
      if(*path.rbegin() != "")
         m_IncludePaths.push_back(path + "");
      else
         m_IncludePaths.push_back(path);
   }
}

const std::map<string,IdlInterface>& MidlParser::GetParsedInterfaces() const
{
   return m_ParsedInterfaces;
}


const std::vector<string>& MidlParser::GetKnownInterfaces() const
{
   return m_KnownInterfaces;
}


// --------------------------------------------------------------------------
// select1st, select2nd
// --------------------------------------------------------------------------
#include <boost/call_traits.hpp>

template <class Pair>
struct select1st : std::unary_function<Pair,typename Pair::first_type>
{
   typename const Pair::first_type& operator()(typename boost::call_traits<Pair>::param_type x) const
   {
      return x.first;
   }
};

template <class Pair>
struct select2nd : std::unary_function<Pair,typename Pair::second_type>
{
   typename const Pair::second_type& operator()(typename boost::call_traits<Pair>::param_type x) const
   {
      return x.second;
   }
};

void MidlParser::Dump(ostream& s) const
{
   s << "KnownInterfaces" << endl << string(30, "=") << endl;
   std::copy(m_KnownInterfaces.begin(), m_KnownInterfaces.end(), std::ostream_iterator<string>(s, "rn"));

   s << endl << "ParsedInterfaces" << endl << string(30, "=") << endl;

   std::copy(
      boost::make_transform_iterator(m_ParsedInterfaces.begin(), select1st<const 
std::map<string, IdlInterface>::value_type>()),
      boost::make_transform_iterator(m_ParsedInterfaces.end(), select1st<const 
std::map<string, IdlInterface>::value_type>()),
      std::ostream_iterator<string>(s, "rn"));


   s.flush();
}
void MidlParser::AddKnownType(const string& t)
{
   if(std::find(m_KnownTypes.begin(), m_KnownTypes.end(), t) ==  m_KnownTypes.end())
   {
      m_KnownTypes.push_back(t);
   }
}
Rodrigo Strauss

Rodrigo Strauss - MCP em Visual C++ e C#. Começou a programar com 12 anos de idade, e desde lá nunca mais parou. Trabalha atualmente desenvolvendo softwares para área financeira usando Visual C++, focando a parte server side e de otimização de performance. Entre seus objetos de estudo estão as linguagens C++ e Python, otimização de performance, e programação kernel mode para plataforma Windows.
Mantém o site
www.1bit.com.br, onde escreve um blog e artigos sobre C++ e programação em geral.