{"id":36843,"date":"2004-12-30T06:58:00","date_gmt":"2004-12-30T06:58:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2004\/12\/30\/using-fibers-to-simplify-enumerators-part-2-when-life-is-easier-for-the-caller\/"},"modified":"2004-12-30T06:58:00","modified_gmt":"2004-12-30T06:58:00","slug":"using-fibers-to-simplify-enumerators-part-2-when-life-is-easier-for-the-caller","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20041230-00\/?p=36843","title":{"rendered":"Using fibers to simplify enumerators, part 2: When life is easier for the caller"},"content":{"rendered":"<p><A HREF=\"\/oldnewthing\/archive\/2004\/12\/29\/343664.aspx\">\nLast time<\/A>,\nwe looked at how a directory tree enumerator function\nwould have been written if the person writing the\nenumerator (the producer)\ngot to write the spec.  Now let&#8217;s look at what\nit would look like if the person consuming the enumerator\nwrote the spec:\n<PRE>\n#include &lt;windows.h&gt;\n#include &lt;shlwapi.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;strsafe.h&gt;<\/p>\n<p>enum FEFOUND {\n FEF_FILE,          \/\/ found a file\n FEF_DIR,           \/\/ found a directory\n FEF_LEAVEDIR,      \/\/ leaving a directory\n FEF_DONE,          \/\/ finished\n};<\/p>\n<p>enum FERESULT {\n FER_CONTINUE,      \/\/ continue enumerating\n                    \/\/ (if directory: recurse into it)\n FER_SKIP,          \/\/ skip directory (do not recurse)\n};<\/p>\n<p>class DirectoryTreeEnumerator {\npublic:\n  DirectoryTreeEnumerator(LPCTSTR pszDir);<\/p>\n<p>  FEFOUND Next();\n  void SetResult(FERESULT fer);\n  void Skip() { SetResult(FER_SKIP); }<\/p>\n<p>  LPCTSTR GetCurDir();\n  LPCTSTR GetCurPath();\n  const WIN32_FIND_DATA* GetCurFindData();\nprivate:\n    &#8230; implementation &#8230;\n};\n<\/PRE>\n<P>\nUnder this design, the enumerator spits out files,\nand the caller tells the enumerator when to move on\nto the next one, optionally indicating that an\nenumerated directory should be skipped rather than\nrecursed into.\n<\/P>\n<P>\nNotice that there is no <CODE>FER_STOP<\/CODE> result code.\nIf the consumer wants to stop enumerating, it will merely\nstop calling <CODE>Next()<\/CODE>.\n<\/P>\n<P>\nWith this design, our test function that computes\nthe inclusive and exclusive sizes of each directory\nis quite simple:\n<\/P>\n<PRE>\nULONGLONG FileSize(const WIN32_FIND_DATA *pwfd)\n{\n  return \n    ((ULONGLONG)pwfd-&gt;nFileSizeHigh &lt;&lt; 32) +\n    pwfd-&gt;nFileSizeLow;\n}<\/p>\n<p>ULONGLONG TestWalk(DirectoryTreeEnumerator* penum)\n{\n ULONGLONG ullSizeSelf = 0;\n ULONGLONG ullSizeAll = 0;\n for (;;) {\n  FEFOUND fef = penum-&gt;Next();\n  switch (fef) {\n  case FEF_FILE:\n   ullSizeSelf += FileSize(penum-&gt;GetCurFindData());\n   break;<\/p>\n<p>  case FEF_DIR:\n   ullSizeAll += TestWalk(penum);\n   break;<\/p>\n<p>  case FEF_LEAVEDIR:\n   ullSizeAll += ullSizeSelf;\n   printf(&#8220;Size of %s is %I64d (%I64d)\\n&#8221;,\n    penum-&gt;GetCurDir(), ullSizeSelf, ullSizeAll);\n   return ullSizeAll;<\/p>\n<p>  case FEF_DONE:\n   return ullSizeAll;\n  }\n }\n \/* notreached *\/\n}<\/p>\n<p>int __cdecl main(int argc, char **argv)\n{\n DirectoryTreeEnumerator e(TEXT(&#8220;.&#8221;));\n TestWalk(&amp;e);\n return 0;\n}\n<\/PRE>\n<P>\nOf course, this design puts all the work on the enumerator.\nInstead of letting the producer walking\nthe tree and calling the callback as it finds things,\nthe caller calls Next() repeatedly, and each time, the\nenumerator has to find the next file and return it.\nSince the enumerator returns, it can&#8217;t store its state in the\ncall stack; instead it has to mimic the call stack manually\nwith a stack data structure.\n<\/P>\n<PRE>\nclass DirectoryTreeEnumerator {\npublic:\n DirectoryTreeEnumerator(LPCTSTR pszDir);\n ~DirectoryTreeEnumerator();<\/p>\n<p> FEFOUND Next();\n void SetResult(FERESULT fer)\n  { m_es = fer == FER_SKIP ? ES_SKIP : ES_NORMAL; }\n void Skip() { SetResult(FER_SKIP); }<\/p>\n<p> LPCTSTR GetCurDir()\n    { return m_pseCur-&gt;m_szDir; }\n LPCTSTR GetCurPath()\n    { return m_szPath; }\n const WIN32_FIND_DATA* GetCurFindData()\n    { return &amp;m_pseCur-&gt;m_wfd; }<\/p>\n<p>private:\n struct StackEntry {\n  StackEntry *m_pseNext;\n  HANDLE m_hfind;\n  WIN32_FIND_DATA m_wfd;\n  TCHAR m_szDir[MAX_PATH];\n };<\/p>\n<p> StackEntry* Push(LPCTSTR pszDir);\n void StopDir();\n bool Stopped();\n void Pop();<\/p>\n<p> enum EnumState {\n  ES_NORMAL,\n  ES_SKIP,\n  ES_FIRST,\n };<\/p>\n<p> StackEntry *m_pseCur;\n EnumState m_es;\n TCHAR m_szPath[MAX_PATH];\n};<\/p>\n<p>DirectoryTreeEnumerator::StackEntry*\nDirectoryTreeEnumerator::Push(\n    LPCTSTR pszDir)\n{\n StackEntry* pse = new StackEntry();\n if (pse &amp;&amp;\n     SUCCEEDED(StringCchCopy(pse-&gt;m_szDir,\n                 MAX_PATH, pszDir)) &amp;&amp;\n     PathCombine(m_szPath, pse-&gt;m_szDir,\n                  TEXT(&#8220;*.*&#8221;)) &amp;&amp;\n     (pse-&gt;m_hfind = FindFirstFile(m_szPath,\n       &amp;pse-&gt;m_wfd)) != INVALID_HANDLE_VALUE) {\n  pse-&gt;m_pseNext = m_pseCur;\n  m_es = ES_FIRST;\n  m_pseCur = pse;\n } else {\n  delete pse;\n  pse = NULL;\n }\n return pse;\n}<\/p>\n<p>void DirectoryTreeEnumerator::StopDir()\n{\n StackEntry* pse = m_pseCur;\n if (pse-&gt;m_hfind != INVALID_HANDLE_VALUE) {\n  FindClose(pse-&gt;m_hfind);\n  pse-&gt;m_hfind = INVALID_HANDLE_VALUE;\n }\n}<\/p>\n<p>bool DirectoryTreeEnumerator::Stopped()\n{\n return m_pseCur-&gt;m_hfind == INVALID_HANDLE_VALUE;\n}<\/p>\n<p>void DirectoryTreeEnumerator::Pop()\n{\n StackEntry* pse = m_pseCur;\n m_pseCur = pse-&gt;m_pseNext;\n delete pse;\n}<\/p>\n<p>DirectoryTreeEnumerator::~DirectoryTreeEnumerator()\n{\n while (m_pseCur) {\n  StopDir();\n  Pop();\n }\n}<\/p>\n<p>DirectoryTreeEnumerator::\n    DirectoryTreeEnumerator(LPCTSTR pszDir)\n : m_pseCur(NULL)\n{\n Push(pszDir);\n}<\/p>\n<p>FEFOUND DirectoryTreeEnumerator::Next()\n{\n for (;;) {\n  \/* Anything to enumerate? *\/\n  if (!m_pseCur) return FEF_DONE;<\/p>\n<p>  \/* If just left a directory, pop *\/\n  if (Stopped()) {\n   Pop();\n   m_es = ES_NORMAL;\n  }<\/p>\n<p>  \/* If accepted a directory, recurse *\/\n  else if (m_es == ES_NORMAL &amp;&amp;\n      (m_pseCur-&gt;m_wfd.dwFileAttributes &amp;\n                      FILE_ATTRIBUTE_DIRECTORY)) {\n   Push(m_szPath);\n  }<\/p>\n<p>  \/* Any more files in this directory? *\/\n  if (m_es != ES_FIRST &amp;&amp;\n       !FindNextFile(m_pseCur-&gt;m_hfind,\n             &amp;m_pseCur-&gt;m_wfd)) {\n   StopDir();\n   return FEF_LEAVEDIR;\n  }<\/p>\n<p>  \/* Don&#8217;t recurse into . or .. *\/\n  if (lstrcmp(m_pseCur-&gt;m_wfd.cFileName,\n                   TEXT(&#8220;.&#8221;)) == 0 ||\n      lstrcmp(m_pseCur-&gt;m_wfd.cFileName,\n                   TEXT(&#8220;..&#8221;)) == 0 ||\n      !PathCombine(m_szPath, m_pseCur-&gt;m_szDir,\n                   m_pseCur-&gt;m_wfd.cFileName)) {\n   m_es = ES_NORMAL;\n   continue;\n  }<\/p>\n<p>  \/* Return this found item *\/\n  m_es = ES_NORMAL; \/* default state *\/\n  if (m_pseCur-&gt;m_wfd.dwFileAttributes &amp;\n                      FILE_ATTRIBUTE_DIRECTORY) {\n   return FEF_DIR;\n  } else {\n   return FEF_FILE;\n  }\n }\n \/* notreached *\/\n}\n<\/PRE>\n<P>\nYuck-o-rama. The simple recursive function has\nturned into this horrible mess of state management.\n<\/P>\n<P>\nWouldn&#8217;t it be great if we could have it both ways?\nThe caller would see a simple enumerator that spits out\nfiles (or directories).  But the enumerator sees a\ncallback that it can throw files into.\n<\/P>\n<P>\nWe&#8217;ll build that next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last time, we looked at how a directory tree enumerator function would have been written if the person writing the enumerator (the producer) got to write the spec. Now let&#8217;s look at what it would look like if the person consuming the enumerator wrote the spec: #include &lt;windows.h&gt; #include &lt;shlwapi.h&gt; #include &lt;stdio.h&gt; #include &lt;strsafe.h&gt; enum [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-36843","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Last time, we looked at how a directory tree enumerator function would have been written if the person writing the enumerator (the producer) got to write the spec. Now let&#8217;s look at what it would look like if the person consuming the enumerator wrote the spec: #include &lt;windows.h&gt; #include &lt;shlwapi.h&gt; #include &lt;stdio.h&gt; #include &lt;strsafe.h&gt; enum [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/36843","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=36843"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/36843\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=36843"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=36843"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=36843"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}